序言
當前專案是通過當時Vue-CLI 2.x
生成使用,配置是babel 6
編譯、webpack 3.x
打包。由於專案發展到某個階段,需要升級優化。目標是打包速度更快、bundle體積更小。
本文通過分享程式碼片段講述部分版本升級後的不同以及可能發生的報錯案例,達到讓小夥伴們能找到對應的解決辦法,減少升級版本的恐懼感。
正文
第一部分 Webpack
1. mode 模式
通過配置 mode
,可選 production
生產環境 和 development
開發環境。
2. 基本配置
-
移除 loaders,使用 rules 代替
// webpack.base.config.js module: { - loaders: {} + rules: {} } 複製程式碼
-
外掛
CommonsChunkPlugin
替換成配置optimization.splitChunks
和optimization.runtimeChunk
參考:webpack.js.org/plugins/spl…
原webpack 3
CommonsChunkPlugin
外掛:new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: 2 }), // webpack 相關程式碼打包到一個檔案 // 新模組加入給新模組加一個id // 規避長快取問題 new webpack.optimize.CommonsChunkPlugin({ name: 'runtime' }) 複製程式碼
現
webpack 4
替代者splitChunks
&runtimeChunk
:optimization: { splitChunks: { cacheGroups: { vendor: { name: 'vendor', minChunks: 2 // 可選 'initial | async | all', // 分別代表,初始化時載入、非同步載入、兩者皆使用 chunks: 'all' // 代表權重值,值越大,打包優先順序越高 priority: 10 } } }, runtimeChunk: { name: 'runtime' } } 複製程式碼
3. 生產環境 production
生產環境下簡化配置,預設開啟外掛 UglifyJsPlugin
// webpack.prod.config.js
module.exports = {
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({
- "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
複製程式碼
當然,生產環境依然可以通過配置關閉 UglifyJsPlugin
optimization: {
minimize: false
}
複製程式碼
4. 開發環境 development
- devServer
webpack3 需要手寫 dev-server.js
webpack4 使用webpack配置// webpack.dev.config.js devServer: { open: true, // 自動開啟瀏覽器頁面 host: 'xxx.xxx.com', // host openPage: 'xxx' // 路徑 historyApiFallback: true // 啟用 history模式 proxy: 'xxx' // 代理配置 } 複製程式碼
5. package.json
由於開發環境的devServer使用方式有變化,所以 package.json
的 scripts
也需要修改,否則會報如下錯誤:
ERROR in Entry module not found: Error: Can't resolve './src' in 'E:\workspace'
正確修改配置:
// package.json
{
"scripts": {
- "dev": "node build/dev-server.js",
+ "dev": "webpack-dev-server --config build/webpack.dev.config.js"
}
}
複製程式碼
5. 外掛安裝/升級
如果小夥伴正在使用以下外掛,請按詳情變更使用,並且可能出現報錯解決:
- vue-loader v15
- thread-loader
- eslint-loader v5
- copy-webpack-plugin
- html-webpack-plugin v4
- mini-csss-extract-plugin 替代 extract-text-webpack-plugin
- script-text-html-webpack-plugin 廢棄
詳細:
-
vue-loader v15
① 從 v14 遷移// webpack.base.config.js const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { // ... plugins: [ new VueLoaderPlugin() ] } 複製程式碼
-
thread-loader
本來打算使用HappyPack
,可是vue-loader@15 不完全支援 HappyPack。
現在這個Issue已經關閉,所以小夥伴可以繼續嘗試用HappyPack
進行優化,記得反饋哦。 -
eslint-loader v5
如有eslint報錯如下:
cannot read property 'eslint' of undefined
新增配置:plugins: [ new webpack.LoaderOptionsPlugin({ options: {} }) ] 複製程式碼
如有警告如下:
[ESLINT_LEGACY_OBJECT_REST_SPREAD] DeprecationWarning. The 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' option is deprecated. Use 'parserOptions.ecmaVersion' instead
修改配置:// .eslintrc.js parserOptions: { parse: 'babel-eslint', ecmaVersion: 8, - ecmaFeatures: { - experimentalObjectRestSpread: true - } } 複製程式碼
-
copy-webpack-plugin
如有報錯如下:TypeError: compilation.contextDependencies.push is not a function
升級外掛npm i copy-webpack-plugin@latest -D 複製程式碼
-
html-webpack-plugin v4 如果小夥伴使用
html-webpack-plugin
提供的鉤子擴充套件了自己定義的外掛,可能會發生以下錯誤:
①Plugin could not be registered at 'html-webpack-plugin-before-html-generation'. Hook was not found. BREAKING CHANGE: There need to exist a hook at 'this.hooks'.
②TypeError: callback is not a function
③this.htmlWebpackPlugin.getHooks is not a function
原因:這三者都是因為升級版本或者使用hooks的方式改變了而導致。
參考:
www.npmjs.com/package/htm…
github.com/jantimon/ht…
解決://plugin.js const HtmlWebpackPlugin = require('html-webpack-plugin'); class MyPlugin { apply (compiler) { compiler.hooks.compilation.tap('MyPlugin', (compilation) => { console.log('The compiler is starting a new compilation...') // Staic Plugin interface |compilation |HOOK NAME | register listener HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync( 'MyPlugin', // <-- Set a meaningful name here for stacktraces (data, cb) => { // Manipulate the content data.html += 'The Magic Footer' // Tell webpack to move on cb(null, data) } ) }) } } module.exports = MyPlugin 複製程式碼
-
mini-csss-extract-plugin
這是在我們的專案升級過程中,最容易出現問題的外掛。
① 修改Vue-CLI 2
生成關於處理樣式的utils.js
如下:// const ExtractTextPlugin = require("extract-text-webpack-plugin"); const MiniCssExtractPlugin = require('mini-css-extract-plugin') ... // 找到: - if (options.extract) { - //options.extract = NODE_ENV === 'production' - return ExtractTextPlugin.extract({ - use: loaders, - fallback: 'vue-style-loader' - }) - } else { - return ['vue-style-lodaer'].concat(loaders)) - } + return [options.extract ? + MiniCssExtractPlugin.loader : + 'vue-style-loader' + ].concat(loaders) 複製程式碼
② 報錯:
document is not defined
解決:區分環境使用 mini-css-extract-plugin(production) 和 vue-style-loader(development),如 ① 配置即可③ 報錯:
cannot read property pop of undefined
原因:是webpack optimize時候產生的,小編也一知半解,不好解釋。
參考:④ 警告:
[mini-css-extract-plugin] Conflicting order between
原因:不同CSS模組裡,引入同一個CSS,而引入的順序不一樣產生的警告。
參考:github.com/webpack-con…
解決:遮蔽警告// 安裝依賴 npm i -D webpack-filter-warnings-plugin // 配置外掛 plugins: { new FilterWarningsPlugin({ exclude: /mini-css-extract-plugin[^]*Conflicting order between:/, }) } 複製程式碼
⑤ 疑惑:打包後,產生很多小的css檔案,能不能把他們都打包成一個css檔案
原因:vue-loader@15 會把<style lang="less">
當作*.less
參考:vue-loader.vuejs.org/zh/migratin…
解決:棄用mini-css-extract-plugin
,重用extract-text-webpack-plugin
並升級版本。且把utils.js
的配置如原來升級前。npm i -D extract-text-webpack-plugin@next 複製程式碼
但是,這裡可能會產生報錯,如下:
Error: Path variable [contenthash:8] not implemented in this context: [name]_[contenthash:8].css
原因:外掛的臨時版本@next
並不支援contenthash
,而且現在這款外掛已經不更新了,所以,最好還是使用mini-css-extract-plugin
。
參考:github.com/webpack-con…
解決:plugins: [ new ExtractTextPlugun({ - filename: '[name].[contenthash:8].css' + filename: '[md5:contenthash:hex:20]' }}) ] 複製程式碼
-
script-text-html-webpack-plugin
原因:外掛暫不支援 webpack4,將會在 v2版 實現
解決:使用preload-webpack-plugin
6. 其他警告或報錯
-
You may need an appropriate loader to handle this file type
原因:package.json
裡安裝依賴變化了,但是由於package-lock.json
存在。
解決辦法:
全域性配置一勞永逸:npm config set package-lock false 複製程式碼
解決當前專案:
刪除package-lock.json
、node_modules
,重新執行命令npm install
-
__webpack_hmr 404 not found
原因:webpack 配置 entry 陣列裡面有build/dev-client
的配置
解決:刪除這個配置即可 -
部分檔案內作用域
this = undefined
原因:不明
解決:this 改為 window -
warn: entrypoint = undefined
解決:不用理會
第二部分 Babel
1. 版本升級
# 不安裝到本地而是直接執行命令,npm 的新功能
npx babel-upgrade --write
# 或者常規方式
npm i babel-upgrade -g
babel-upgrade --write
# 更新 babel 配置 並且 安裝依賴
npx babel-upgrade --write --install
複製程式碼
2. 配置檔案
這裡區分需不需要編譯 node_modules
裡面的依賴。
如果需要,刪除專案根目錄下 .babelrc
改為使用 babel.config.js
3. polyfill
- 推薦使用
@babel/preset-env
並按需引入polyfill
babel 7:@babel/polyfill
babel 6:babel-polyfill
Promise
等ES6語法,在 Android 4.4以下 和 IE 的相容問題// node 環境 require('@babel/polyfill') // ES6 main.js import('@babel/polyfill') // webpack.base.config.js entry: ['@babel/polyfill', 'main.js'] 複製程式碼