├── .babelrc
├── .eslintrc.json
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── src
├── app.js
├── html
│ ├── app.html
│ └── index.html
├── images
│ ├── icon-left.png
│ ├── icon-qq.png
│ └── icon-wechat.png
├── index.js
├── js
│ └── util.js
└── sass
│ ├── app.scss
│ ├── footer.scss
│ ├── header.scss
│ ├── index.scss
│ └── main.scss
├── webpack.config.build.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-3"]
3 | }
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "ecmaVersion": 6,
4 | "sourceType": "script"
5 | },
6 | // 具体规则参阅 http://eslint.cn/docs/rules/
7 | "rules": {
8 | // 要求 return 语句要么总是指定返回的值,要么不指定
9 | "consistent-return": 1,
10 | // 强制分号
11 | "semi": 1,
12 | // default-case
13 | "default-case": 1,
14 | // 要求使用 === 和 !==
15 | "eqeqeq": 1,
16 | // 禁止出现空函数
17 | "no-empty-function": 1
18 | }
19 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dest
2 | node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # webpack_demo
2 |
3 | a webpack3 scaffold .
4 |
5 | 基于 Webpack3 的脚手架工具。
6 |
7 | ## 构建方式
8 |
9 | 提供三种构建方式,用于不同开发场景
10 |
11 | + npm run dev 开发
12 | - 生成一个web服务器,开启实时热更新。
13 |
14 | + npm run test 测试
15 | - 使用了 `--watch` 参数,实现开发时资源的增量更新输出。
16 |
17 | + npm run build 线上
18 | - 资源压缩
19 | - 静态资源可选择带上 hash 戳值
20 | - 多页面提取公共静态资源
21 | - 作用域提升(webpack3新功能)
22 |
23 | ## 使用 webpack 进行开发
24 |
25 | + 热替换实时刷新
26 | + 允许错误不打断程序
27 | + 实时语法检测
28 | + 打包后文件体积分析
29 | + 分包拆包
30 |
31 | ## 使用 webpack 打包可实现功能点
32 |
33 | 一个页面一个入口文件。
34 |
35 | 针对各类资源模块,可实现下列功能:
36 |
37 | ### CSS
38 |
39 | + 可选内联 CSS 或者单独抽离 CSS 文件
40 | + CSS 文件合并、压缩
41 | + 使用 sass-loader,可使用 sass 语法
42 | + 可配置 postcss,自动添加前缀(autoprefixer)
43 | + 打包生成的 css 样式文件可选添加 hash 戳值
44 |
45 | ### images
46 | + 图片压缩
47 | + 可设定引用图片大小的阈值,小于这个阈值大小的图片将编码成 base64
48 | + 引用图片时添加图片的 hash 值
49 |
50 | ### javascript
51 | + 合并、压缩
52 | + ES6语法(babel-loader)
53 | + eslint(代码检测),可自定义检测 rules,或者引入(airbnb规范)
54 | + 打包生成的 js 样式文件可选添加 hash 戳值
55 |
56 | ### html
57 | + 美化压缩,可选删除注释、空白符与换行
58 | + 打包生成新的独立的 html 文件
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack_demo",
3 | "version": "1.0.0",
4 | "description": "webpack demo",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack-dev-server --config webpack.config.js",
8 | "test": "cross-env NODE_ENV=test webpack --watch --progress --hide-modules",
9 | "build": "webpack --config ./webpack.config.build.js --progress --hide-modules"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "autoprefixer": "^7.1.2",
15 | "autoprefixer-loader": "^3.2.0",
16 | "babel-core": "^6.0.0",
17 | "babel-loader": "^7.1.1",
18 | "babel-polyfill": "^6.20.0",
19 | "babel-preset-es2015": "^6.0.0",
20 | "babel-preset-stage-3": "^6.17.0",
21 | "cross-env": "^3.0.0",
22 | "css-loader": "^0.23.1",
23 | "eslint": "^4.2.0",
24 | "eslint-loader": "^1.9.0",
25 | "extract-text-webpack-plugin": "^3.0.0",
26 | "extract-text-webpack-plugin-webpack-2": "^1.0.1",
27 | "file-loader": "^0.8.5",
28 | "html-loader": "^0.4.3",
29 | "html-webpack-plugin": "^2.29.0",
30 | "node-sass": "^4.5.3",
31 | "path": "^0.12.7",
32 | "postcss-loader": "^0.9.1",
33 | "postcss-uncss": "^0.15.0",
34 | "postcss-write-svg": "jonathantneal/postcss-write-svg",
35 | "sass-loader": "^6.0.6",
36 | "style-loader": "^0.13.0",
37 | "uncss": "^0.15.0",
38 | "url-loader": "^0.5.7",
39 | "webpack": "^3.3.0",
40 | "webpack-dev-server": "^2.5.1"
41 | },
42 | "dependencies": {
43 | "webpack-bundle-analyzer": "^2.11.1",
44 | "postcss-uncss": "^0.15.0",
45 | "uncss": "^0.15.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | require('./html/app.html');
2 | require('./sass/app.scss');
3 | require('./js/util');
4 |
5 | var bar = 123;
6 | var foo = 456;
--------------------------------------------------------------------------------
/src/html/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | app
10 |
11 |
12 |
13 |
14 | 测试多页面打包页
15 | 抽离公用js模块
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Webpack Demo
10 |
11 |
12 |
13 |
14 | Webpack Demo
15 | 单独抽离 CSS 文件
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/images/icon-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chokcoco/webpack_demo/c986c354b17593ed0d618fc7ca319e351f6dd636/src/images/icon-left.png
--------------------------------------------------------------------------------
/src/images/icon-qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chokcoco/webpack_demo/c986c354b17593ed0d618fc7ca319e351f6dd636/src/images/icon-qq.png
--------------------------------------------------------------------------------
/src/images/icon-wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chokcoco/webpack_demo/c986c354b17593ed0d618fc7ca319e351f6dd636/src/images/icon-wechat.png
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | require('./html/index.html');
2 | require('./sass/index.scss');
3 |
4 | require('./js/util');
5 |
6 | var bar = 123456;
7 | var foo = 456789;
--------------------------------------------------------------------------------
/src/js/util.js:
--------------------------------------------------------------------------------
1 | function deepCopy(obj) {
2 | var newObj = obj.constructor === Array ? [] : {};
3 |
4 | for (var key in obj) {
5 | newObj[key] = typeof obj[key] === "object" ? deepCopy(obj[key]) : obj[key];
6 | }
7 |
8 | return newObj;
9 | }
10 |
11 | function isRotateStr(bar, foo) {
12 | if(bar.length != foo.length) {
13 | return false;
14 | }
15 |
16 | var baz = bar + bar;
17 |
18 | if(baz.indexOf(foo) != -1) {
19 | return true
20 | }else {
21 | return false
22 | }
23 | }
24 |
25 | function strSort(str) {
26 | return str.split("").sort((a, b) => a.charCodeAt() > b.charCodeAt()).join("");
27 | }
28 |
29 | function split(str, boz) {
30 | var arr = [];
31 | var bozLength = boz.length;
32 | var pointer = 0;
33 |
34 | while(str.indexOf(boz) != -1) {
35 | var index = str.indexOf(boz);
36 | arr.push(str.substr(pointer, index));
37 | str = str.slice(index + bozLength);
38 | }
39 |
40 | arr.push(str);
41 |
42 | return arr;
43 | }
44 |
45 | function searchClass(element) {
46 | let classes = [];
47 |
48 | if(element.getAttribute('class')) {
49 | arr = arr.concat(element.getAttribute('class').split(' '));
50 | }
51 |
52 | Array.prototype.slice.call(element.children).forEach((elem) => {
53 | searchClass(elem);
54 | });
55 |
56 | return new Set(arr);
57 | }
--------------------------------------------------------------------------------
/src/sass/app.scss:
--------------------------------------------------------------------------------
1 | @import './main'
--------------------------------------------------------------------------------
/src/sass/footer.scss:
--------------------------------------------------------------------------------
1 | h2 {
2 | color: yellowgreen;
3 | }
--------------------------------------------------------------------------------
/src/sass/header.scss:
--------------------------------------------------------------------------------
1 | body,
2 | html {
3 | position: relative;
4 | user-select: none;
5 | }
6 |
7 | h1 {
8 | color: #333;
9 |
10 | span {
11 | color: deeppink;
12 | transform: rotate(10deg);
13 | user-select: none;
14 | }
15 | }
16 |
17 | .avator {
18 | width: 160px;
19 | height: 160px;
20 | background: url('http://pic.cnblogs.com/avatar/608782/20160411131806.png');
21 | }
22 |
23 | .logo {
24 | width: 160px;
25 | height: 160px;
26 | background: url('../images/icon-wechat.png');
27 | }
28 |
29 | .icon {
30 | width: 8px;
31 | height: 16px;
32 | background: url('../images/icon-left.png');
33 | }
34 |
35 | .bor {
36 | width: 100px;
37 | height: 100px;
38 | background-color: #333;
39 | }
--------------------------------------------------------------------------------
/src/sass/index.scss:
--------------------------------------------------------------------------------
1 | @import './header';
2 | @import './footer';
3 |
--------------------------------------------------------------------------------
/src/sass/main.scss:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: #336699;
3 | font-weight: normal;
4 | }
--------------------------------------------------------------------------------
/webpack.config.build.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var path = require('path');
3 | var fs = require('fs');
4 | var publicPath = "";
5 | var ExtractTextPlugin = require("extract-text-webpack-plugin");
6 | var HtmlWebpackPlugin = require('html-webpack-plugin');
7 | var autoprefixer = require('autoprefixer');
8 | var publicPath = '//test.tv.video.qq.com/ktweb/Public/activity/';
9 |
10 | module.exports = {
11 | //页面入口文件配置
12 | entry: {
13 | index: './src/index'
14 | },
15 | output: {
16 | path: path.resolve(__dirname, './dest'),
17 | filename: 'js/[name].bundle.[hash:8].js',
18 | publicPath: publicPath
19 | },
20 | module: {
21 | //加载器配置
22 | loaders: [{
23 | test: /\.css$/,
24 | loader: 'style-loader!css-loader?minimize'
25 | }, {
26 | test: /\.scss$/,
27 | use: ExtractTextPlugin.extract({
28 | fallback: 'style-loader',
29 | use: ['css-loader?minimize', 'postcss-loader', 'sass-loader']
30 | })
31 | }, {
32 | test: /\.(png|jpg|gif)$/,
33 | use: [
34 | {
35 | loader: 'url-loader',
36 | options: {
37 | limit: 2048,
38 | name: 'images/[name].[ext]?[hash:8]'
39 | }
40 | }
41 | ]
42 | }, {
43 | test: /\.(html)$/,
44 | loader: 'html-loader?attrs=img:src'
45 | }, {
46 | test:/\.(js)$/,
47 | exclude: /node_modules/,
48 | loader: 'babel-loader'
49 | }]
50 | },
51 | //插件项
52 | plugins: [
53 | new webpack.LoaderOptionsPlugin({
54 | options: {
55 | postcss: function() {
56 | return [
57 | autoprefixer({
58 | browsers: ['last 2 versions', 'Android >= 4.0']
59 | })
60 | ];
61 | }
62 | }
63 | }),
64 | // 作用域提升
65 | new webpack.optimize.ModuleConcatenationPlugin(),
66 | // 提取代码中的公共模块
67 | // new webpack.optimize.CommonsChunkPlugin(),
68 | // 单独抽离 CSS
69 | new ExtractTextPlugin('css/[name].bundle.[hash:8].css'),
70 | // 定义全局变量插件
71 | new webpack.DefinePlugin({
72 | 'process.env': {
73 | NODE_ENV: 'production'
74 | }
75 | }),
76 | // JS 压缩插件
77 | new webpack.optimize.UglifyJsPlugin({
78 | compress: {
79 | warnings: false
80 | }
81 | }),
82 | // 生成最终HTML
83 | new HtmlWebpackPlugin({
84 | filename: 'html/index.html',
85 | template: './src/html/index.html',
86 | inject: false,
87 | hash: true,
88 | minify: {
89 | //移除HTML中的注释
90 | removeComments: true,
91 | //删除空白符与换行符
92 | collapseWhitespace: false
93 | }
94 | })
95 | ],
96 | devtool: 'cheap-module-source-map'
97 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var path = require('path');
3 | var fs = require('fs');
4 | var ExtractTextPlugin = require("extract-text-webpack-plugin");
5 | var autoprefixer = require('autoprefixer');
6 | var HtmlWebpackPlugin = require('html-webpack-plugin');
7 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
8 | var host = '127.0.0.1';
9 | var port = '8080';
10 | var publicPath = 'http://' + host + ':' + port + '/';
11 |
12 | module.exports = {
13 | //页面入口文件配置
14 | entry: {
15 | index: './src/index',
16 | app: './src/app'
17 | },
18 | output: {
19 | path: path.resolve(__dirname, './dest'),
20 | filename: 'js/[name].bundle.js',
21 | publicPath: publicPath
22 | },
23 | module: {
24 | //加载器配置
25 | loaders: [{
26 | test: /\.css$/,
27 | loader: 'style-loader!css-loader'
28 | }, {
29 | test: /\.scss$/,
30 | use: ExtractTextPlugin.extract({
31 | fallback: 'style-loader',
32 | use: [{
33 | loader: 'css-loader',
34 | options: {
35 | minimize: true
36 | }
37 | },{
38 | loader: 'postcss-loader'
39 | },{
40 | loader: 'sass-loader'
41 | }]
42 | })
43 | }, {
44 | test: /\.(png|jpg|gif)$/,
45 | use: [{
46 | loader: 'url-loader',
47 | options: {
48 | limit: 2048,
49 | name: 'images/[name].[ext]?[hash]'
50 | }
51 | }]
52 | }, {
53 | test: /\.(html)$/,
54 | loader: 'html-loader?attrs=img:src'
55 | }, {
56 | test: /\.(js)$/,
57 | exclude: /(node_modules|lib)/,
58 | use: [
59 | "babel-loader",
60 | "eslint-loader"
61 | ],
62 | }]
63 | },
64 | //插件项
65 | plugins: [
66 | // 启用作用域提升(scope hoisting)
67 | new webpack.optimize.ModuleConcatenationPlugin(),
68 | // 部分 loader 配置信息
69 | new webpack.LoaderOptionsPlugin({
70 | options: {
71 | postcss: function () {
72 | return [
73 | autoprefixer({
74 | browsers: ['last 2 versions', 'Android >= 4.0'],
75 | //是否美化属性值 默认:true
76 | cascade: true,
77 | //是否去掉不必要的前缀 默认:true
78 | remove: true
79 | })
80 | ];
81 | }
82 | }
83 | }),
84 | // 提取代码中的公共模块
85 | new webpack.optimize.CommonsChunkPlugin({
86 | name: "commons",
87 | filename: "js/common/commons.js",
88 | // Only use these entries
89 | chunks: ["app", "index"]
90 | }),
91 | // 作用域提升
92 | new webpack.optimize.ModuleConcatenationPlugin(),
93 | // 代码热替换
94 | new webpack.HotModuleReplacementPlugin(),
95 | // 允许错误不打断程序
96 | new webpack.NoEmitOnErrorsPlugin(),
97 | // 单独抽离 CSS
98 | new ExtractTextPlugin('css/[name].bundle.css'),
99 | // 生成最终HTML
100 | new HtmlWebpackPlugin({
101 | filename: 'html/index.html',
102 | template: './src/html/index.html',
103 | inject: false,
104 | hash: true,
105 | minify: {
106 | // 移除HTML中的注释
107 | removeComments: true,
108 | // 删除空白符与换行符
109 | collapseWhitespace: false
110 | }
111 | }),
112 | new HtmlWebpackPlugin({
113 | filename: 'html/app.html',
114 | template: './src/html/app.html',
115 | inject: false,
116 | hash: true,
117 | minify: {
118 | // 移除HTML中的注释
119 | removeComments: true,
120 | // 删除空白符与换行符
121 | collapseWhitespace: false
122 | }
123 | }),
124 | // 打包模块大小追踪
125 | new BundleAnalyzerPlugin()
126 | ],
127 | devtool: 'source-map',
128 | devServer: {
129 | host: host,
130 | port: port,
131 | // gzip
132 | compress: true,
133 | // 不跳转
134 | historyApiFallback: false,
135 | // 实时刷新
136 | inline: true,
137 | // 隐藏 webpack 包 bundle 信息,错误和警告仍然会显示。
138 | noInfo: false
139 | }
140 | };
141 |
--------------------------------------------------------------------------------