详细的配置可以参考官网:https://doc.webpack-china.org/guides/
一开始做项目时都是直接从组里前辈搭建好的脚手架开始写代码,到后来自己写新项目时又是拷贝之前的工程作为脚手架开始。对于脚手架本身却不甚了解,不仅不思考为什么更是没有改进的想法,怪不得工作满一年了却总觉得自己的技术水平在原地踏步,就是没有总结和思考。
目前组里的技术栈都是使用vue+koa,使用webpack的好处一是方便写vue的单文件组件,二是打包文件方便生产部署再加上能无所顾虑的应用语言的新特性。
1. 配置文件
官方文档推荐写webpack配置文件时,先写出一个基本配置文件包含入口、输出等,再根据开发/生产环境所需要插件的不同,利用webpack-merge生成两个配置文件。
package.json 依赖参考
"devDependencies": { "babel-core": "^6.25.0", "babel-loader": "^7.1.1", "css-loader": "^0.28.4", "less": "^2.7.2", "less-loader": "^4.0.4", "style-loader": "^0.18.2", "vue-loader": "^13.0.0", "webpack": "^3.0.0", "webpack-chunk-hash": "^0.4.0", "webpack-dev-server": "^2.5.0", "webpack-manifest-plugin": "^1.1.2", "webpack-merge": "^4.1.0" }
webpack.config.base.js
1 const path = require(‘path‘); 2 const webpack = require(‘webpack‘); 3 const WebpackManifestPlugin = require("webpack-manifest-plugin"); 4 const WebpackChunkHash = require("webpack-chunk-hash"); 5 6 module.exports = { 7 entry: { 8 index: ‘./app/src/index.js‘, 9 }, 10 output: { 11 path: path.resolve(__dirname, ‘..‘, ‘app‘, ‘public‘, ‘js‘) 12 }, 13 resolve: { 14 alias: { 15 ‘vue$‘: path.resolve(__dirname, ‘../node_modules/vue/dist/vue.js‘), 16 } 17 }, 18 module: { 19 loaders: [ 20 { 21 test: /\.vue$/, 22 loader: ‘vue-loader‘, 23 }, 24 { 25 test: /\.js$/, 26 exclude: /node_modules/, 27 loader: ‘babel-loader‘, 28 }, 29 { 30 test: /\.css$/, 31 use: [ ‘style-loader‘, ‘css-loader‘ ] 32 }, 33 { 34 test: /\.less$/, 35 loader: "style!css!less" 36 } 37 ] 38 }, 39 plugins: [ 40 new WebpackManifestPlugin(), 41 new webpack.optimize.CommonsChunkPlugin({ 42 name: ‘vendor‘, 43 minChunks: function (module) { 44 // this assumes your vendor imports exist in the node_modules directory 45 return module.context && module.context.indexOf(‘node_modules‘) !== -1; 46 } 47 }), 48 //CommonChunksPlugin will now extract all the common modules from vendor and main bundles 49 new webpack.optimize.CommonsChunkPlugin({ 50 name: ‘manifest‘ //But since there are no more common modules between them we end up with just the runtime code included in the manifest file 51 }), 52 new WebpackChunkHash(), 53 ], 54 }
第15行代码可以参考这里和这里,默认NPM包导出的是运行时构建,Vue2的运行时构建不支持单文件组件的template
第40行的 WebpackManifestPlugin 作用是将输出文件名保存在文件中 (当输出文件名带 chunkhash 时很有用,参考这里)
第41、49行的 CommonsChunkPlugin 作用是从打包后的 bundle 文件中提取公共模块,将 npm install 的公共模块和业务代码分开,这样浏览器就可以一直缓存公共模块的bundle,参考这里
第52行的 WebpackChunkHash 作用是让文件名的哈希值基于文件内容
webpack.config.dev.js
1 const path = require(‘path‘); 2 const webpack = require(‘webpack‘); 3 const Merge = require(‘webpack-merge‘); 4 const baseConfig = require(‘./webpack.config.base.js‘); 5 6 module.exports = Merge(baseConfig, { 7 output: { 8 filename: ‘[name].js‘, 9 }, 10 devtool: ‘#eval-source-map‘, 11 plugins: [ 12 new webpack.HotModuleReplacementPlugin() 13 ], 14 devServer: { 15 port: 7002, 16 hot: true 17 } 18 });
第10行设置source-map,方便用浏览器查看源代码
第12、14行主要用于使用下面会提到的webpack-dev-server
webpack.config.prod.js
1 const webpack = require(‘webpack‘); 2 const Merge = require(‘webpack-merge‘); 3 const baseConfig = require(‘./webpack.config.base.js‘); 4 5 module.exports = Merge(baseConfig, { 6 output: { 7 filename: ‘[name].[chunkhash].js‘, 8 }, 9 plugins: [ 10 new webpack.optimize.UglifyJsPlugin({ 11 compress: { 12 warnings: false 13 } 14 }) 15 ] 16 });
第10行 UglifyJsPlugin 的作用是压缩、混淆代码
webpack的配置文件自定义程度很高,所以在参考他人配置时最好能弄清楚为什么要这样写
2. webpack-dev-server
在开发过程中另一个重要的东西是 webpack-dev-server,它的作用是当你改动源代码后能自动重新打包,再加上 webpack 的HMR-模块热替换特性,这样改动代码就能直接在浏览器里看到效果,省却了代码手动打包+刷新浏览器的步骤。使用 webpack-dev-server 有 CLI 和 API 两种使用方法。
CLI 也就是我目前所用的方法,需要设置好配置文件,参考上面的 dev 文件中的 HotModuleReplacementPlugin 和 devServer 设置:devServer 的port 为 webpack-dev-server 运行的端口,hot 设置和 HotModuleReplacementPlugin 启用 HMR。启动的命令为:
node_modules/.bin/webpack-dev-server --config ./config/webpack.config.dev.js
然后通过 http://localhost:7002/index.js 就能获取到打包后对应的文件内容,注意文件是在内存,端口和文件名参考配置文件。
要想在网页中引用打包后的文件可以通过后端使用 nunjucks 之类的模板将文件地址注入到网页中,这里有好几个需要注意的地方。
一个是打包后的 bundle 文件需要注意引入的顺序,公共模块文件要先引入进来,我的开发环境引入顺序为:
<script src="http://localhost:7002/manifest.js"></script> <script src="http://localhost:7002/vendor.js"></script> <script src="http://localhost:7002/index.js"></script>
否则会报 ReferenceError: webpackJsonp is not defined 错误
二是注意要有 .babelrc 文件:
{ "presets": ["es2015"] }
否则会报 SyntaxError: Unexpected token: name (xxxxxx) from Uglify plugin 之类的错误,无法识别语言新特性
三是在生成环境中,由于输出文件的文件名带 chunkhash,会随着文件内容的改变而改变,所以需要在后端读取 manifest.json 文件的内容,并将文件名通过 nunjucks 之类的模板引擎注入到网页中。当然可以不使用 chunkhash,这样就可以将文件名在网页里写死了,但这样就不方便利用到浏览器的缓存。
3. webpack-dev-middleware
当使用 express、koa 之类的后端框架时可以利用 webpack-dev-middleware,通过中间件来实现热加载。具体使用方法:这里