詳細的配置能夠參考官網:https://doc.webpack-china.org/guides/css
一開始作項目時都是直接從組裏前輩搭建好的腳手架開始寫代碼,到後來本身寫新項目時又是拷貝以前的工程做爲腳手架開始。對於腳手架自己卻不甚瞭解,不只不思考爲何更是沒有改進的想法,怪不得工做滿一年了卻總以爲本身的技術水平在原地踏步,就是沒有總結和思考。前端
目前組裏的技術棧都是使用vue+koa,使用webpack的好處一是方便寫vue的單文件組件,二是打包文件方便生產部署再加上能無所顧慮的應用語言的新特性。vue
1. 配置文件node
官方文檔推薦寫webpack配置文件時,先寫出一個基本配置文件(base)包含入口、輸出等,再根據開發/生產環境所須要插件的不一樣,利用webpack-merge生成三個配置文件: dev、prod、analyzewebpack
package.json 依賴參考git
"devDependencies": { "babel-core": "^6.18.2", "babel-loader": "^7.1.1", "babel-preset-es2015": "^6.18.0", "css-loader": "^0.28.4", "koa-webpack-dev-middleware": "^1.4.0", "koa-webpack-hot-middleware": "^1.0.3", "less": "^2.7.1", "less-loader": "^4.0.5", "style-loader": "^0.18.2", "url-loader": "^0.5.7", "vue-loader": "^12.2.2", "webpack": "^3.3.0", "webpack-bundle-analyzer": "^2.8.3", "webpack-dev-middleware": "^1.11.0", "webpack-dev-server": "^2.6.1", "webpack-hot-middleware": "^2.13.1", "webpack-koa-hot-middleware": "^0.1.2", "webpack-manifest-plugin": "^1.2.1", "webpack-merge": "^4.1.0" }
webpack.config.base.jsgithub
1 'use strict' 2 let path = require('path'); 3 let webpack = require('webpack'); 4 let WebpackManifestPlugin = require("webpack-manifest-plugin"); 5 6 module.exports = { 7 output: { 8 path: path.resolve(__dirname, '..', 'build') 9 }, 10 resolve: { 11 extensions: ['.js', '.vue'], 12 modules: ['node_modules'], 13 alias: { 14 'leafletCSS': 'leaflet/dist/leaflet.css', 15 'leaflet$': 'leaflet/dist/leaflet.js', 16 'vue$': 'vue/dist/vue.min.js', 17 'vue-resource$': 'vue-resource/dist/vue-resource.min.js' 18 } 19 }, 20 module: { 21 rules: [ 22 { 23 test: /\.vue$/, 24 loader: 'vue-loader', 25 }, 26 { 27 test: /\.js$/, 28 exclude: /(node_modules|bower_components)/, 29 use: { 30 loader: 'babel-loader', 31 options: { 32 presets: ['es2015'] 33 } 34 } 35 }, 36 { 37 test: /\.css$/, 38 use: [ 39 'style-loader', 40 'css-loader' 41 ] 42 }, 43 { 44 test: /\.(png|jpe?g|gif|svg|woff2?|ttf|otf)(\?.*)?$/, 45 loader: 'url-loader', 46 }, 47 { 48 test: /\.less$/, 49 use: [ 50 'style-loader', 51 'css-loader', 52 'less-loader' 53 ] 54 } 55 ] 56 }, 57 plugins: [ 58 new WebpackManifestPlugin(), 59 new webpack.optimize.CommonsChunkPlugin({ 60 name: 'vendor', 61 minChunks: function (module) { 62 // this assumes your vendor imports exist in the node_modules directory 63 return module.context && module.context.indexOf('node_modules') !== -1; 64 } 65 }), 66 new webpack.optimize.CommonsChunkPlugin({ 67 name: 'common', 68 chunks: ['qq', 'navi', 'log', 'guide', 'apply', 'voice', 'pianhang', 'dynamic'], //這裏輸入須要提取公共代碼的entry 69 minChunks: 2 70 }), 71 //CommonChunksPlugin will now extract all the common modules from vendor and main bundles 72 new webpack.optimize.CommonsChunkPlugin({ 73 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 74 }), 75 ] 76 }
第16行代碼能夠參考這裏和這裏,默認NPM包導出的是運行時構建,Vue2的運行時構建不支持單文件組件的templateweb
第58行的 WebpackManifestPlugin 做用是將輸出文件名保存在文件中 (當輸出文件名帶 chunkhash 時頗有用,參考這裏)npm
第59~74行的 CommonsChunkPlugin 做用是從打包後的 bundle 文件中提取公共模塊,將 npm install 的公共模塊和業務代碼分開,這樣瀏覽器就能夠一直緩存公共模塊的bundle,參考這裏。第一個 CommonsChunkPlugin 做用是將 node_modules 裏的模塊提取到 vendor.js 裏;第二個 CommonsChunkPlugin 做用是將 entry 裏的公共代碼提取出來放在 common.js 裏,參考這裏;第三個 CommonsChunkPlugin 做用是將 webpack 運行時代碼放在 manifest.js 裏json
webpack.config.dev.js
1 "use strict" 2 let webpack = require('webpack'); 3 let merge = require('webpack-merge'); 4 let base_config = require('./webpack.config.base'); 5 6 module.exports = merge(base_config, { 7 entry: { 8 qq: ['./src/qq/qq.js', 'webpack-hot-middleware/client'], 9 navi: ['./src/navi/navi.js', 'webpack-hot-middleware/client'], 10 log: ['./src/log/log.js', 'webpack-hot-middleware/client'], 11 guide: ['./src/guide/guide.js', 'webpack-hot-middleware/client'], 12 apply: ['./src/apply/apply.js', 'webpack-hot-middleware/client'], 13 voice: ['./src/voice/voice.js', 'webpack-hot-middleware/client'], 14 pianhang: ['./src/pianhang/pianhang.js', 'webpack-hot-middleware/client'], 15 dynamic: ['./src/dynamic/dynamic.js', 'webpack-hot-middleware/client'] 16 }, 17 output: { 18 filename: '[name].js', 19 }, 20 devtool: '#eval-source-map', 21 plugins: [ 22 new webpack.HotModuleReplacementPlugin(), 23 ] 24 });
第 20 行設置source-map,方便用瀏覽器查看源代碼
webpack.config.prod.js
1 "use strict" 2 let webpack = require('webpack'); 3 let merge = require('webpack-merge'); 4 let base_config = require('./webpack.config.base'); 5 6 module.exports = merge(base_config, { 7 entry: { 8 qq: ['./src/qq/qq.js'], 9 navi: ['./src/navi/navi.js'], 10 log: ['./src/log/log.js'], 11 guide: ['./src/guide/guide.js'], 12 apply: ['./src/apply/apply.js'], 13 voice: ['./src/voice/voice.js'], 14 pianhang: ['./src/pianhang/pianhang.js'], 15 dynamic: ['./src/dynamic/dynamic.js'], 16 }, 17 output: { 18 filename: '[name].[chunkhash].js', 19 }, 20 plugins: [ 21 new webpack.optimize.UglifyJsPlugin() 22 ] 23 });
第 21 行 UglifyJsPlugin 的做用是壓縮、混淆代碼
webpack.config.analyze.js
1 "use strict" 2 3 let webpack = require('webpack'); 4 let merge = require('webpack-merge'); 5 var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 6 let prod_config = require('./webpack.config.prod'); 7 8 module.exports = merge(prod_config, { 9 plugins: [ 10 new BundleAnalyzerPlugin() 11 ] 12 });
analyze 文件的做用利用 webpack-bundle-analyzer 插件分析打包狀況,
webpack --config ./config/webpack.config.analyze.js
運行結果以下:
webpack的配置文件自定義程度很高,因此在參考他人配置時最好能弄清楚爲何要這樣寫
2. webpack-dev-server
在開發過程當中另外一個重要的東西是 webpack-dev-server,它的做用是當你改動源代碼後能自動從新打包,再加上 webpack 的HMR-模塊熱替換特性,這樣改動代碼就能直接在瀏覽器裏看到效果,省卻了代碼手動打包+刷新瀏覽器的步驟。使用 webpack-dev-server 有 CLI 和 API 兩種使用方法。
CLI 方式設置 dev 文件中的 HotModuleReplacementPlugin 和 devServer 啓用 HMR。啓動的命令爲:
webpack-dev-server --config ./config/webpack.config.dev.js
API 方式直接在命令裏設置參數,如:(這裏用到了 Unix Domin Socket,也能夠直接指定 ip和端口)
webpack-dev-server --config config/webpack.dev.config.js --public 0.0.0.0:8056 --progress --inline --hot --socket .dev-shared/sockets/webpack.sock
webpack-dev-server 的方式配置簡單,缺點是引入 bundle 比較麻煩,須要指定其它端口
3. webpack-dev-middleware
上述配置文件就是使用的該方法,須要有 koa-webpack-dev-middleware、koa-webpack-hot-middleware(熱更新)。而後在 index.js 裏寫:
1 if (process.env.NODE_ENV == 'dev') { 2 let webpack = require('webpack'); 3 let webpackConfig = require('./config/webpack.config.dev.js'); 4 let webpackDevMiddleware = require('koa-webpack-dev-middleware'); 5 let webpackHotMiddleware = require('koa-webpack-hot-middleware'); 6 let compiler = webpack(webpackConfig); 7 app.use(webpackDevMiddleware(compiler)); 8 app.use(webpackHotMiddleware(compiler)); 9 }
這樣就不須要經過額外的端口獲取 bundle 文件了,注意這裏是 koa 環境
4. 如何在前端框架裏引入 bundle
因爲 webpack prod 配置文件裏使用了 chunkhash 做爲 bundle 的名字的一部分,修改業務代碼,chunkhash 會發生改變,因此須要經過一些方法自動將 bundle 名字注入到前端頁面裏:
第一種方法是經過在後端 controller 裏讀取 manifest.json 裏的內容,而後經過模板引擎,注入到頁面裏,例如 nunjucks:
{% for path in paths %} <script src="{{path}}"></script> {% endfor %}
第二種方法是拓展模板引擎命令,例如 xtpl:
{{{ xScript('manifest.js') }}} {{{ xScript('vendor.js') }}} {{{ xScript('common.js') }}} {{{ xScript('apply.js') }}}
xtpl 命令拓展現例 xtpl.ext.js :
1 let xtplApp = require('xtpl/lib/koa'); 2 let xtpl = require('xtpl/lib/xtpl'); 3 let XTemplate = xtpl.XTemplate; 4 5 XTemplate.addCommand('xScript', function(scope, option){ 6 let name = option.params[0]; 7 if (process.env.NODE_ENV !== 'dev') { 8 let assets_map = require('./manifest'); 9 name = assets_map[name]; 10 } 11 return '<script src="' + name + '" ></script>'; 12 }); 13 14 module.exports = xtplApp;
而後在 index.js 裏
let xtpl = require('./extensions/xtpl.ext');
注意引入 bundle 的時候要注意引入順序:manifest > vendor > common > entry,不然可能會報 ReferenceError: webpackJsonp is not defined 錯誤,還要注意要有 .babelrc 文件:
{ "presets": ["es2015"] }
不然會報 SyntaxError: Unexpected token: name (xxxxxx) from Uglify plugin 之類的錯誤,沒法識別語言新特性