├── .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 | --------------------------------------------------------------------------------