0. 前言
早在 2016 年我就發佈過一篇關於在多頁面下使用 Webpack + Vue 的配置的文章,當時也是我在做自己一個個人項目時遇到的配置問題,想到別人也可能遇到跟我同樣的問題,就把配置的思路分享出來了,傳送門在這裏。
因爲那份配置直到現在還有人在關注,同時最近公司幫助項目升級了 Webpack 4,趁機也把之前的配置也升級了一下,而且博客荒廢了這麼久,都快 9102 年了,不能連年均一篇博文都不到,所以有了下面的分享。
下面的配置主要是給在多頁面下使用 Webpack 的同學在升級 Webpack 時提供一點思路,多頁面的配置思路請點擊上面的傳送門。
下面代碼的地址 https://github.com/cnu4/Webpack-Vue-MultiplePage
1. Webpack 升級 4.x
1.1. 升級和安裝相關依賴
- webpack 升級
- webpack-cli webapck4.x 需要新加的依賴
- mini-css-extract-plugin 取代 extract-text-webpack-plugin
-
其他相關 loader 和 plugin
- css-loader
- file-loader
- url-loader
- vue-style-loader
- vue-template-compiler(注意要保持與 vue 版本一直)
- html-webpack-plugin@next
1.2 修改配置
mode 構建模式
設置 mode 構建模式,比如 development 會將 process.env.NODE_ENV 的值設爲 development
mini-css-extract-plugin
刪除原 extract-text-webpack-plugin 配置,增加 mini-css-extract-plugin 配置
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' ] },
]
}
}
optimization
這是 webpack 4 一個比較大的變動點,webpack 4 中刪除了 webpack.optimize.CommonsChunkPlugin
,並且使用 optimization
中的splitChunk
來替代,下面的配置代替了之前的 CommonsChunkPlugin 配置,同意能提取 JS 和 CSS 文件
module.exports = {
optimization: {
splitChunks: {
vendors: {
name: 'venders',
chunks: 'all',
minChunks: chunks.length
}
}
}
vue-loader 升級
vue-loader 15 注意要配合一個 webpack 插件才能正確使用
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
plugins: [ new VueLoaderPlugin() ]
}
html-webpack-plugin 升級
升級到 next
,否則開發下無法正常注入資源文件
文件壓縮
- optimize-css-assets-webpack-plugin
- terser-webpack-plugin
壓縮的配置也移動到了 optimization 選項下,值得注意的是壓縮工具換成了 terser-webpack-plugin,這是 webpack 官方也推薦使用的,估計在 webpack 5 中會變成默認的配置,實測打包速度確實變快了很多。
配置
module.exports = {
minimizer: [
new TerserPlugin({ // 壓縮js
cache: true,
parallel: true
}
}),
new OptimizeCSSAssetsPlugin({ // 壓縮css
cssProcessorOptions: {
safe: true
}
})
]
}
}
2. 增加 ES6+ 支持
2.1 安裝依賴
- "babel-core": "^6.26.3",
- "babel-loader": "^7.1.5",
- "babel-plugin-transform-runtime": "^6.23.0",
- "babel-preset-env": "^1.7.0",
- "babel-preset-stage-2": "^6.24.1",
- "babel-runtime": "^6.26.0",
2.2 添加配置文件 .babelrc
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "ie >= 9"]
},
"useBuiltIns": "usage"
}],
"stage-2"
],
"plugins": ["transform-runtime"]
}
2.3 增加 webpack 配置
module.exports = {
modules: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
}
}
2.4 更新 eslint 配置
3. 打包速度優化
可以使用下面的插件看看打包時間主要耗時在哪
3.1 TerserPlugin 開啓 parallel 選項
開啓多線程
3.2 HappyPack 和 thread-loader 開啓 Loader 多進程轉換
github 的 Demo 中沒有引入,有興趣的同學可以嘗試,在一些耗時的 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' ]
})
];
3.3 提前打包公共代碼
DllPlugin
使用 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 包。下面插件可以自動完成這些操作。
AutoDllPlugin
安裝依賴 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']
}
}));
3.4 terser-webpack-plugin
webpack 官方推薦使用的 JS 壓縮插件,取代 UglifyJS,大幅提高打包速度
4. 其他問題
下面是我公司項目中遇到的問題,各位升級過程中如果遇到同樣的問題可以參考一下解決思路。
4.1 json-loader
json-loader
webpack4 內置的json-loader 有點兼容性問題,安裝 json-loader 依賴和更改配置
解決:
{
test: /\.json$/, //用於匹配loaders所處理文件拓展名的正則表達式
use: 'json-loader', //具體loader的名稱
type: 'javascript/auto',
exclude: /node_modules/
}
4.2 vue-loader
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 屬性,這樣就不用改動所有代碼,我們在項目中也是這樣處理的。
4.3 提取公共 css 代碼
scss 中 import 的代碼不能被提取到公共 css 中
解決:改到 js 中引入就可以,詳見下面 issue
mini-css-extract-plugin + sass-loader + splitChunks · Issue #49
4.4 mini-css-extract-plugin filename 不支持函數
mini-css-extract-plugin 的 filename 選項不支持函數,所以只能轉用其他方式解決
解決:使用插件 FileManagerPlugin 在構建後移動文件,等 filename 支持函數後再優化