在 vue-cli3
發佈以前,使用 vue-cli2
構建的 vue
項目是基於 webpack3.x
的,伴隨着項目的版本迭代,功能逐漸增多,文件也逐漸增多,打包時間從最初的 4.5
分鐘,最久的時候 17
分鐘。javascript
由於使用 ts
的緣故,每次打包 ts-loader
須要進行類型檢查,致使耗時劇增。權衡利弊下,將 ts-loader
的類型檢查禁用後(transpileOnly
選項設置爲 true
),打包時間銳減到 1
分鐘。css
隨着時間的推移,項目依賴逐漸落後,打包時間也到了 2.5
分鐘,恰逢 webpack4
對於打包性能的提高,因而就有了這篇升級日誌:html
總體思路:vue
一開始的項目依賴升級的思路是,先升級 webpack
到最新的版本,而後逐步升級 loader
、plugin
、babel
等依賴。可是基於以前其餘項目升級 webpack4
的經驗,發現逐步升級會耗費一部分時間在新舊依賴的問題上,因而決定激進一點,使用 ncu
一次性將 package.json
中包含的項目依賴升級到最新版本,考慮到不肯定性,能夠選擇性的修改 package.json
不升級某些依賴,好比主要版本號有變更的依賴(eg: 0.x.x => 1.x.x)。java
# 安裝 npm-check-updates
npm i -g npm-check-updates
# 檢查項目依賴的版本與最新穩定版本,列出清單(beta版本可能不會列出來)
ncu
# 將 `package.json` 中包含的依賴的版本號,更新到最新
ncu -u
# 刪除舊的依賴,安裝新的依賴
rm -rf node_modules
rm package-lock.json
npm i
複製代碼
npm run dev
複製代碼
運行過程當中遇到的問題:node
報錯日誌:webpack
/Users/yourProjectPath/build/dev-server.js:47
app.use(proxyMiddleware(options.filter || context, options));
^
TypeError: proxyMiddleware is not a function
at /Users/yourProjectPath/build/dev-server.js:47:11
at Array.forEach (<anonymous>)
at Object.<anonymous> (/Users/yourProjectPath/build/dev-server.js:42:25)
at Module._compile (module.js:643:30)
at Object.Module._extensions..js (module.js:654:10)
at Module.load (module.js:556:32)
at tryModuleLoad (module.js:499:12)
at Function.Module._load (module.js:491:3)
at Function.Module.runMain (module.js:684:10)
at startup (bootstrap_node.js:187:16)
at bootstrap_node.js:608:3
複製代碼
緣由:git
由於 http-proxy-middleware 升級到 v1.x
致使的:es6
- "http-proxy-middleware": "~0.17.3",
+ "http-proxy-middleware": "~1.0.3",
複製代碼
v1.x
起,須要經過主動導入 createProxyMiddleware
的方式使用中間件:github
// javascript
const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
const app = express();
app.use(
"/api",
createProxyMiddleware({
target: "http://www.example.org",
changeOrigin: true,
})
);
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
複製代碼
如何解決:
主動導入 createProxyMiddleware
- const proxyMiddleware = require('http-proxy-middleware');
+ const { createProxyMiddleware } = require('http-proxy-middleware');
複製代碼
將 proxyMiddleware
改成 createProxyMiddleware
- app.use(proxyMiddleware(options.filter || context, options));
+ app.use(createProxyMiddleware(options.filter || context, options));
複製代碼
報錯日誌:
ERROR in ./src/App.vue
Module Error (from ./node_modules/vue-loader/lib/index.js):
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
@ ./src/main.ts 2:0-28 45:23-26
@ multi ./build/dev-client ./src/main.ts
複製代碼
ERROR in ./src/page/supplier/preferential/full-discount/view.vue?vue&type=script&lang=ts&
Module not found: Error: Can't resolve '../../../../interface/coupon.coupon_code_view' in '/Users/yourProjectPath/src/page/supplier/preferential/full-discount'
@ ./src/page/supplier/preferential/full-discount/view.vue?vue&type=script&lang=ts& 19:0-70
@ ./src/page/supplier/preferential/full-discount/view.vue
@ ./src/router/supplier/preferential.ts
@ ./src/router/supplier/index.ts
@ ./src/router/index.ts
@ ./src/main.ts
@ multi ./build/dev-client ./src/main.ts
複製代碼
緣由:
vue-loader
升級到 v15.x
之後,須要搭配 VueLoaderPlugin
使用
如何解決:
// webpack.base.confg.js
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
// ...
plugins: [new VueLoaderPlugin()],
};
複製代碼
報錯日誌:
Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):
(Emitted value instead of an instance of Error)
Errors compiling template:
Component template requires a root element, rather than just text.
1 |
|
2 | #app
| ^^^^
3 | router-view
| ^^^^^^^^^^^^^
@ ./src/App.vue?vue&type=template&id=7ba5bd90&lang=pug& 1:0-238 1:0-238
@ ./src/App.vue
@ ./src/main.ts
@ multi ./build/dev-client ./src/main.ts
複製代碼
Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):
(Emitted value instead of an instance of Error)
Errors compiling template:
text "div
el-card" outside root element will be ignored.
複製代碼
緣由:
引用自 vue-loader
文檔:
注意有些模板的 loader 會導出一個編譯好的模板函數而不是普通的 HTML,諸如 pug-loader。爲了向 Vue 的模板編譯器傳遞正確的內容,你必須換用一個輸出普通 HTML 的 loader。例如,爲了支持
<template lang="pug">
,你可使用 pug-plain-loader:
如何解決:
項目中使用到了 pug
語法,因爲 vue-loader
的升級,如今須要引入 pug-plain-loader
npm i -D pug-plain-loader
複製代碼
// webpack.base.confg.js
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.pug$/,
loader: "pug-plain-loader"
}
];
}
};
複製代碼
報錯日誌:
ERROR in ./src/store/mutation/util.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find module '@babel/core'
babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'.
at Function.Module._resolveFilename (module.js:538:15)
at Function.Module._load (module.js:468:25)
at Module.require (module.js:587:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (/Users/yourProjectPath/node_modules/babel-loader/lib/index.js:10:11)
at Module._compile (module.js:643:30)
at Object.Module._extensions..js (module.js:654:10)
at Module.load (module.js:556:32)
at tryModuleLoad (module.js:499:12)
at Function.Module._load (module.js:491:3)
at Module.require (module.js:587:17)
at require (internal/module.js:11:18)
at loadLoader (/Users/yourProjectPath/node_modules/loader-runner/lib/loadLoader.js:18:17)
at iteratePitchingLoaders (/Users/yourProjectPath/node_modules/loader-runner/lib/LoaderRunner.js:169:2)
at runLoaders (/Users/yourProjectPath/node_modules/loader-runner/lib/LoaderRunner.js:365:2)
at NormalModule.doBuild (/Users/yourProjectPath/node_modules/webpack/lib/NormalModule.js:295:3)
@ ./src/store/mutation/nav.ts 3:0-48 54:41-63 59:41-63 63:41-63 70:37-59
@ ./src/store/mutation/index.ts
@ ./src/store/index.ts
@ ./src/main.ts
@ multi ./build/dev-client ./src/main.ts
複製代碼
緣由:
根據錯誤提示能夠知道,babel-loader@8
依賴 Babel 7.x
(也就是 @babel/core
)。
如何解決:
卸載 babel-core
npm un babel-core
複製代碼
安裝 @babel/core
npm i -D @babel/core
複製代碼
報錯日誌:
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: /Users/yourProjectPath/src/page/image-tag/bus.js: Cannot read property 'bindings' of null
at Scope.moveBindingTo (/Users/yourProjectPath/node_modules/@babel/traverse/lib/scope/index.js:926:13)
at BlockScoping.updateScopeInfo (/Users/yourProjectPath/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:364:17)
at BlockScoping.run (/Users/yourProjectPath/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:330:12)
at PluginPass.BlockStatementSwitchStatementProgram (/Users/yourProjectPath/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:70:24)
at newFn (/Users/yourProjectPath/node_modules/@babel/traverse/lib/visitors.js:179:21)
at NodePath.\_call (/Users/yourProjectPath/node_modules/@babel/traverse/lib/path/context.js:55:20)
at NodePath.call (/Users/yourProjectPath/node_modules/@babel/traverse/lib/path/context.js:42:17)
at NodePath.visit (/Users/yourProjectPath/node_modules/@babel/traverse/lib/path/context.js:90:31)
at TraversalContext.visitQueue (/Users/yourProjectPath/node_modules/@babel/traverse/lib/context.js:112:16)
at TraversalContext.visitSingle (/Users/yourProjectPath/node_modules/@babel/traverse/lib/context.js:84:19)
at TraversalContext.visit (/Users/yourProjectPath/node_modules/@babel/traverse/lib/context.js:140:19)
at Function.traverse.node (/Users/yourProjectPath/node_modules/@babel/traverse/lib/index.js:84:17)
at traverse (/Users/yourProjectPath/node_modules/@babel/traverse/lib/index.js:66:12)
at transformFile (/Users/yourProjectPath/node_modules/@babel/core/lib/transformation/index.js:107:29)
at transformFile.next (<anonymous>)
at run (/Users/yourProjectPath/node_modules/@babel/core/lib/transformation/index.js:35:12)
複製代碼
緣由:
因爲已經將 Babel
升級到 7.x
,可是 .babelrc
仍在引用適用於 Babel 6.x
的 babel-preset-env
如何解決:
卸載 babel-preset-env
npm un babel-preset-env
複製代碼
安裝 @babel/preset-env
npm i -D @babel/preset-env
複製代碼
從 .babelrc
中移除 babel-preset-env
,改用 @babel/preset-env
- { "presets": ["env"] }
+ { "presets": ["@babel/preset-env"] }
複製代碼
報錯日誌:
ERROR in ./src/lib/image-drop.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: /Users/yourProjectPath/src/lib/image-drop.js: this.setDynamic is not a function
at PluginPass.pre (/Users/yourProjectPath/node_modules/babel-plugin-transform-runtime/lib/index.js:31:12)
at transformFile (/Users/yourProjectPath/node_modules/@babel/core/lib/transformation/index.js:96:27)
at transformFile.next (<anonymous>)
at run (/Users/yourProjectPath/node_modules/@babel/core/lib/transformation/index.js:35:12)
at run.next (<anonymous>)
at Function.transform (/Users/yourProjectPath/node_modules/@babel/core/lib/transform.js:27:41)
at transform.next (<anonymous>)
at step (/Users/yourProjectPath/node_modules/gensync/index.js:254:32)
at /Users/yourProjectPath/node_modules/gensync/index.js:266:13
at async.call.result.err.err (/Users/yourProjectPath/node_modules/gensync/index.js:216:11)
at /Users/yourProjectPath/node_modules/gensync/index.js:184:28
at /Users/yourProjectPath/node_modules/@babel/core/lib/gensync-utils/async.js:72:7
at /Users/yourProjectPath/node_modules/gensync/index.js:108:33
at step (/Users/yourProjectPath/node_modules/gensync/index.js:280:14)
at /Users/yourProjectPath/node_modules/gensync/index.js:266:13
at async.call.result.err.err (/Users/yourProjectPath/node_modules/gensync/index.js:216:11)
複製代碼
緣由:
因爲已經將 Babel
升級到 7.x
,可是 .babelrc
仍在引用適用於 Babel 6.x
的 babel-plugin-transform-runtime
如何解決:
卸載 babel-plugin-transform-runtime
npm un babel-plugin-transform-runtime
複製代碼
安裝 @babel/plugin-transform-runtime
npm i -D @babel/plugin-transform-runtime
複製代碼
從 .babelrc
中移除 babel-preset-env
,改用 @babel/plugin-transform-runtime
- { "plugins": ["babel-plugin-transform-runtime"] }
+ { "plugins": ["@babel/plugin-transform-runtime"] }
複製代碼
報錯日誌:
ERROR in ./node_modules/element-ui/lib/theme-chalk/index.css (./node_modules/css-loader/dist/cjs.js??ref--11-1!./node_modules/element-ui/lib/theme-chalk/index.css)
Module build failed (from ./node_modules/css-loader/dist/cjs.js):
ValidationError: Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options has an unknown property 'minimize'. These properties are valid:
object { url?, import?, modules?, sourceMap?, importLoaders?, localsConvention?, onlyLocals? }
at validate (/Users/yourProjectPath/node_modules/css-loader/node_modules/schema-utils/dist/validate.js:50:11)
at Object.loader (/Users/yourProjectPath/node_modules/css-loader/dist/index.js:34:28)
@ ./node_modules/element-ui/lib/theme-chalk/index.css 4:14-81 14:3-18:5 15:22-89
@ ./src/main.ts
@ multi ./build/dev-client ./src/main.ts
複製代碼
緣由:
minimize
配置項被移除致使
如何解決:
刪除 css-loader
的 minimize
選項
報錯日誌:
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
複製代碼
緣由:
webpack4
開始,提供了 mode
選項用來啓用 webpack
提供的內置選項,沒有設置時,默認值爲 production
如何解決:
開發環境設置:development
,生成環境設置:production
至此已經能夠跑通開發環境打包了
運行過程當中遇到的問題:
報錯日誌:
/Users/yourProjectPath/node_modules/webpack/lib/webpack.js:189
throw new RemovedPluginError(errorMessage);
^
Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
at Object.get [as CommonsChunkPlugin] (/Users/yourProjectPath/node_modules/webpack/lib/webpack.js:189:10)
at Object.<anonymous> (/Users/yourProjectPath/build/webpack.prod.conf.js:85:26)
at Module._compile (module.js:643:30)
at Object.Module._extensions..js (module.js:654:10)
at Module.load (module.js:556:32)
at tryModuleLoad (module.js:499:12)
at Function.Module._load (module.js:491:3)
at Module.require (module.js:587:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (/Users/yourProjectPath/build/build.js:12:23)
at Module._compile (module.js:643:30)
at Object.Module._extensions..js (module.js:654:10)
at Module.load (module.js:556:32)
at tryModuleLoad (module.js:499:12)
at Function.Module._load (module.js:491:3)
at Function.Module.runMain (module.js:684:10)
複製代碼
緣由:
根據錯誤提示信息能夠知道,webpack4
開始 CommonsChunkPlugin
被刪除
如何解決:
將 CommonsChunkPlugin
相關的配置刪除,改用 optimization.splitChunks
配置項
The default configuration was chosen to fit web performance best practices, but the optimal strategy for your project might differ. If you're changing the configuration, you should measure the impact of your changes to ensure there's a real benefit. webpack.js.org/plugins/spl…
根據插件文檔的描述,這裏咱們沒有額外增長配置,使用的是 optimization.splitChunks
配置項的默認值。
// webpack.config.js
module.exports = {
// ...
optimization: {
splitChunks: {
chunks: "async",
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: "~",
automaticNameMaxLength: 30,
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
複製代碼
報錯日誌:
/Users/yourProjectPath/node_modules/webpack/lib/Chunk.js:866
throw new Error(
^
Error: Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
at Chunk.get (/Users/yourProjectPath/node_modules/webpack/lib/Chunk.js:866:9)
at /Users/yourProjectPath/node_modules/extract-text-webpack-plugin/dist/index.js:176:48
at Array.forEach (<anonymous>)
at /Users/yourProjectPath/node_modules/extract-text-webpack-plugin/dist/index.js:171:18
at AsyncSeriesHook.eval [as callAsync] (eval at create (/Users/yourProjectPath/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:7:1)
at AsyncSeriesHook.lazyCompileHook (/Users/yourProjectPath/node_modules/tapable/lib/Hook.js:154:20)
at Compilation.seal (/Users/yourProjectPath/node_modules/webpack/lib/Compilation.js:1342:27)
at compilation.finish.err (/Users/yourProjectPath/node_modules/webpack/lib/Compiler.js:675:18)
at hooks.finishModules.callAsync.err (/Users/yourProjectPath/node_modules/webpack/lib/Compilation.js:1261:4)
at AsyncSeriesHook.eval [as callAsync] (eval at create (/Users/yourProjectPath/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:24:1)
at AsyncSeriesHook.lazyCompileHook (/Users/yourProjectPath/node_modules/tapable/lib/Hook.js:154:20)
at Compilation.finish (/Users/yourProjectPath/node_modules/webpack/lib/Compilation.js:1253:28)
at hooks.make.callAsync.err (/Users/yourProjectPath/node_modules/webpack/lib/Compiler.js:672:17)
at _done (eval at create (/Users/yourProjectPath/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:9:1)
at _err1 (eval at create (/Users/yourProjectPath/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:32:22)
at _addModuleChain (/Users/yourProjectPath/node_modules/webpack/lib/Compilation.js:1185:12)
複製代碼
緣由:
extract-text-webpack-plugin 在 webpack4
的時候被廢棄,改用mini-css-extract-plugin
如何解決:
卸載 extract-text-webpack-plugin
npm un extract-text-webpack-plugin
複製代碼
安裝 mini-css-extract-plugin
npm i -D mini-css-extract-plugin
複製代碼
將 extract-text-webpack-plugin
相關的配置刪除,改用 mini-css-extract-plugin
// utils.js
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
if (options.extract) {
- return ExtractTextPlugin.extract({
- use: loaders,
- fallback: 'vue-style-loader'
- });
+ return [
+ {
+ loader: MiniCssExtractPlugin.loader,
+ options: {
+ hmr: process.env.NODE_ENV === 'development'
+ }
+ }
+ ].concat(loaders);
} else {
return ['vue-style-loader'].concat(loaders);
}
複製代碼
// webpack.prod.conf.js
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// ...
plugins: [
- new ExtractTextPlugin({
+ new MiniCssExtractPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
}),
]
}
複製代碼
若是不是使用 vue-cli 2.x
搭建的項目,可能須要這樣修改:
詳情見:webpack.js.org/plugins/min…
// webpack.prod.conf.js
// ***
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [
// ***
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === "development",
},
},
"css-loader",
],
},
],
},
plugins: [
// ***
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
}),
],
};
複製代碼
warnings
is not a supported option報錯日誌:
ERROR in static/js/170.b2935281265dd9ec23b7.js from UglifyJs
DefaultsError: `warnings` is not a supported option
at DefaultsError.get (eval at <anonymous> (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/node_modules/uglify-js/tools/node.js:18:1), <anonymous>:71:23)
at Function.buildError (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/index.js:105:54)
at results.forEach (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/index.js:258:52)
at Array.forEach (<anonymous>)
at taskRunner.run (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/index.js:233:17)
at step (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/TaskRunner.js:85:9)
at done (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/TaskRunner.js:96:30)
at boundWorkers (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/TaskRunner.js:101:13)
at TaskRunner.boundWorkers (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/TaskRunner.js:70:11)
at enqueue (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/TaskRunner.js:91:14)
at tasks.forEach (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/TaskRunner.js:111:9)
at Array.forEach (<anonymous>)
at TaskRunner.run (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/TaskRunner.js:89:11)
at UglifyJsPlugin.optimizeFn (/Users/yourProjectPath/node_modules/uglifyjs-webpack-plugin/dist/index.js:227:18)
at AsyncSeriesHook.eval [as callAsync] (eval at create (/Users/yourProjectPath/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:31:1)
at AsyncSeriesHook.lazyCompileHook (/Users/yourProjectPath/node_modules/tapable/lib/Hook.js:154:20)
複製代碼
緣由:
因爲 UglifyJs
升級致使的,uglifyjs-webpack-plugin
依賴於 UglifyJs
如何解決:
將 uglifyOptions.compress
選項刪除
// webpack.prod.conf.js
new UglifyJsPlugin({
uglifyOptions: {
- compress: {
warnings: false,
drop_console: true,
drop_debugger: true
- }
}
}),
複製代碼
報錯日誌:
ERROR in static/js/app.2c7ff4a31971e22c9397.js from UglifyJs
Unexpected token: keyword «const» [static/js/app.2c7ff4a31971e22c9397.js:3865,0]
複製代碼
緣由:
uglifyjs-webpack-plugin
不支持 es6
語法
如何解決:
卸載 uglifyjs-webpack-plugin
npm un uglifyjs-webpack-plugin
複製代碼
將 uglifyjs-webpack-plugin
相關的配置刪除,不用額外增長配置,由於 production
模式下,webpack
默認使用 terser-webpack-plugin
來最小化 JavaScript
文件
至此經歷了一系列的各類問題以後,開發和生產環境打包都已經能夠跑通了。這篇文章記錄了的兩個 webpack 3.x
項目升級到 webpack 4.x
時遇到的問題、緣由和解決方法,但願對大家有幫助。
升級後生產環境打包速度有所提高,可是文件大小並不理想,下一篇我會講如何對 webpack
的打包進行優化。