早在 2016 年我就發佈過一篇關於在多頁面下使用 Webpack + Vue 的配置的文章,當時也是我在作本身一個我的項目時遇到的配置問題,想到別人也可能遇到跟我一樣的問題,就把配置的思路分享出來了,傳送門在這裏。javascript
由於那份配置直到如今還有人在關注,同時最近公司幫助項目升級了 Webpack 4,趁機也把以前的配置也升級了一下,並且博客荒廢了這麼久,都快 9102 年了,不能連年均一篇博文都不到,因此有了下面的分享。css
下面的配置主要是給在多頁面下使用 Webpack 的同窗在升級 Webpack 時提供一點思路,多頁面的配置思路請點擊上面的傳送門。html
下面代碼的地址 https://github.com/cnu4/Webpack-Vue-MultiplePagevue
其餘相關 loader 和 pluginjava
設置 mode 構建模式,好比 development 會將 process.env.NODE_ENV 的值設爲 developmentnode
刪除原 extract-text-webpack-plugin 配置,增長 mini-css-extract-plugin 配置webpack
module.exports = { plugins: [ new MiniCssExtractPlugin({ filename: 'css/[name].css' }), ], } module.exports = { module: { rules: [ { test:/\.vue$/, loader: 'vue-loader', }, { test: /\.css$/, use: [ // 開發模式下使用 vue-style-loader,以便使用熱重載 process.env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader' ] }, ] } }
這是 webpack 4 一個比較大的變更點,webpack 4 中刪除了 webpack.optimize.CommonsChunkPlugin
,而且使用 optimization
中的splitChunk
來替代,下面的配置代替了以前的 CommonsChunkPlugin 配置,贊成能提取 JS 和 CSS 文件ios
module.exports = { optimization: { splitChunks: { vendors: { name: 'venders', chunks: 'all', minChunks: chunks.length } } }
vue-loader 15 注意要配合一個 webpack 插件才能正確使用git
const { VueLoaderPlugin } = require('vue-loader') module.exports = { plugins: [ new VueLoaderPlugin() ] }
升級到 next
,不然開發下沒法正常注入資源文件github
壓縮的配置也移動到了 optimization 選項下,值得注意的是壓縮工具換成了 terser-webpack-plugin,這是 webpack 官方也推薦使用的,估計在 webpack 5 中會變成默認的配置,實測打包速度確實變快了不少。
配置
module.exports = { minimizer: [ new TerserPlugin({ // 壓縮js cache: true, parallel: true } }), new OptimizeCSSAssetsPlugin({ // 壓縮css cssProcessorOptions: { safe: true } }) ] } }
可使用下面的插件看看打包時間主要耗時在哪
TerserPlugin 壓縮插件能夠開啓多線程,見上面配置
github 的 Demo 中沒有引入,有興趣的同窗能夠嘗試,在一些耗時的 Loader 確實能夠提升速度
vue-loader 不支持 HappyPack,官方建議用 thread-loader
const HappyPack = require('happypack'); exports.module = { rules: [ { test: /.js$/, // 1) replace your original list of loaders with "happypack/loader": // loaders: [ 'babel-loader?presets[]=es2015' ], use: 'happypack/loader', include: [ /* ... */ ], exclude: [ /* ... */ ] } ] }; exports.plugins = [ // 2) create the plugin: new HappyPack({ // 3) re-add the loaders you replaced above in #1: loaders: [ 'babel-loader?presets[]=es2015' ] }) ];
使用 DllPlugn 將 node_modules 或者本身編寫的不常變的依賴包打一個 dll 包,提升速度和充分利用緩存。至關於 splitChunks 提取了公共代碼,但 DllPlugn 是手動指定了公共代碼,提早打包好,免去了後續 webpack 構建時的從新打包。
首先須要增長一個 webpack 配置文件 webpack.dll.config.js
專門針對 dll 打包配置,其中用到 webpack.DllPlugin
。
執行 webpack --config build/webpack.dll.config.js
後,webpack會自動生成 2 個文件,其中vendor.dll.js 即合併打包後第三方模塊。另一個 vendor-mainifest.json 存儲各個模塊和所需公用模塊的對應關係。
接着修改咱們的 webpack 配置文件,在 plugin 配置中增長 webpack.DllReferencePlugin
,配置中指定上一步生成的 json 文件,而後手動在 html 文件中引用上一步的 vendor.dll.js 文件。
後面若是增刪 dll 中的依賴包時都須要手動執行上面打包命令來更新 dll 包。下面插件能夠自動完成這些操做。
安裝依賴 autodll-webpack-plugin
AutoDllPlugin 自動同時至關於完成了 DllReferencePlugin 和 DllPlugin 的工做,只須要在咱們的 webpack 中添加配置。AutoDllPlugin 會在執行 npm install / remove / update package-name
或改變這個插件配件時從新打包 dll。須要注意的是改變 dll 中指定的依賴包不會觸發自動從新打包 dll。
實際打包中生成環境是沒問題的,但開發模式下在有緩存的狀況下,autodll 插件不會生成新的文件,致使 404,因此在 Demo 中暫時關了這個插件。不過 dll 提早打包了公共文件,確實能夠提升打包速度,有興趣的同窗能夠研究下開發模式下的緩存問題,歡迎在評論中分享。
module.exports.plugins.push(new AutoDllPlugin({ inject: true, // will inject the DLL bundles to html context: path.join(__dirname, '.'), filename: '[name].dll.js', debug: true, inherit: true, // path: './', plugins: [ new TerserPlugin({ cacheL true, parallel: true }), new MiniCssExtractPlugin({ filename: '[name].css' }) ], entry: { vendor: ['vue/dist/vue.esm.js', 'axios', 'normalize.css'] } }));
因爲項目中是第一次配置 babel,一步到位直接使用新版 7,新版 babel 使用新的命名空間 @babel,若是是老項目升級 babel 7,可使用工具 babel-upgrade,讀一下 升級文檔
這裏說下上面依賴的做用和升級 babel 7 的改動。
新版中 @babel/runtime 只包含了一些 helpers,若是須要 core-js polyfill 瀏覽器不支持的 API,能夠用 transform 提供的選項 {"corejs": 2}
並安裝依賴 @babel/runtime-corejs2
。即便默認的 polyfill 沒了,但 @babel/plugin-transform-runtime 依然能夠爲咱們分離輔助函數,減小代碼體積
使用 @babel/runtime 的 polyfill 不會污染全局 API,由於不會改動原生對象的原型,它只是建立一個輔助函數在當前做用於生效,因此諸如 [1, 2].includes(1)
這樣的語法也沒法被 polyfill。若是不是開發第三方庫,可使用 @babel/polyfill,相反他的 polyfill 會影響到瀏覽器全局的對象原型
@babel/preset-env 提供了一個 useBuiltIns 選項來按需引入 polyfill,而不須要引入所有。另外一種方法是直接引用 core-js 包下的特定 polyfill。
如今須要手動安裝 @babel/plugin-proposal 開頭的依賴是由於 babel 在新版中移除了 stage presets,爲的是後續更好維護處於 proposal 階段的語法。想要使用 proposal 階段的語法須要單獨引用對應的 plugin, 上面的配置只加了幾個處於 stage 3 階段的 plugin,老項目建議使用 babel-upgrade 升級,自動添加依賴
{ "presets": [ [ "@babel/preset-env", { "modules": false, "targets": { "browsers": [ "> 1%", "last 2 versions", "ie >= 11" ] }, "useBuiltIns": "usage" // 按需引入 polyfill } ] ], "plugins": [ "@babel/plugin-transform-runtime", "@babel/plugin-syntax-dynamic-import", ["@babel/plugin-proposal-class-properties", { "loose": false }], ["@babel/plugin-proposal-decorators", { "legacy": true }], ] }
module.exports = { modules: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] } }
下面是我公司項目中遇到的問題,各位升級過程當中若是遇到一樣的問題能夠參考一下解決思路。
webpack4 內置的json-loader 有點兼容性問題,安裝 json-loader 依賴和更改配置
解決:
{ test: /\.json$/, //用於匹配loaders所處理文件拓展名的正則表達式 use: 'json-loader', //具體loader的名稱 type: 'javascript/auto', exclude: /node_modules/ }
vue-loader 升級到 15.x 後,會致使舊的 commonjs 寫法加載有問題,須要使用 require('com.vue').default
的方式引用組件
13的版本還能夠設置 esModule,14 之後的版本不能設置了,vue 文件導出的模塊必定是 esModule
解決:使用 require('com.vue').default
或者 import
的方式引用組件
esModule option stopped working in version 14 · Issue #1172 · vuejs/vue-loader · GitHub
尤大大建議能夠本身寫一個 babel 插件,遇到 require vue 文件的時候自動加上 default 屬性,這樣就不用改動全部代碼,咱們在項目中也是這樣處理的。
scss 中 import 的代碼不能被提取到公共 css 中。scss 中的 @import 是使用 sass-loader 處理的,處理後已經變成 css 文件,webpack 已經不能判斷是不是同一個模塊,因此不能提取到公共的 css 中,但多頁面中咱們仍是但願一些公共的 css 能被提取到公共的文件中。
解決:將須要提取到公共文件的 css 改到 js 中引入就能夠,詳見下面 issue
mini-css-extract-plugin + sass-loader + splitChunks · Issue #49
mini-css-extract-plugin 的 filename 選項不支持函數,但咱們有時候仍是但願能單獨控制公共 css 文件的位置,而不是和其餘入口文件的 css 使用同樣的目錄格式
解決:使用插件 FileManagerPlugin 在構建後移動文件,等 filename 支持函數後再優化