Vue-CLI2項目從 babel 6 + webpack 3.x 升級到 babel7 + webpack4.x 踩坑

序言

      當前項目是經過當時Vue-CLI 2.x生成使用,配置是babel 6編譯、webpack 3.x打包。因爲項目發展到某個階段,須要升級優化。目標是打包速度更快、bundle體積更小。
      本文經過分享代碼片斷講述部分版本升級後的不一樣以及可能發生的報錯案例,達到讓小夥伴們能找到對應的解決辦法,減小升級版本的恐懼感。css

正文

第一部分 Webpack

1. mode 模式

經過配置 mode,可選 production生產環境 和 development 開發環境。html

2. 基本配置

  1. 移除 loaders,使用 rules 代替vue

    // webpack.base.config.js
    module: {
    -    loaders: {}
    +    rules: {}
    }
    複製代碼
  2. 插件 CommonsChunkPlugin 替換成配置 optimization.splitChunksoptimization.runtimeChunk
    參考:webpack.js.org/plugins/spl…
    webpack 3 CommonsChunkPlugin插件:node

    new webpack.optimize.CommonsChunkPlugin({
         name: 'vendor',
         minChunks: 2
     }),
     // webpack 相關代碼打包到一個文件
     // 新模塊加入給新模塊加一個id
     // 規避長緩存問題
     new webpack.optimize.CommonsChunkPlugin({
         name: 'runtime'
     })
    複製代碼

    webpack 4 替代者 splitChunks & runtimeChunkwebpack

    optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {
                    name: 'vendor',
                    minChunks: 2
                    // 可選 'initial | async | all',
                    // 分別表明,初始化時加載、異步加載、二者皆使用
                    chunks: 'all'  
                    // 表明權重值,值越大,打包優先級越高
                    priority: 10 
                }
            }
        },
        runtimeChunk: {
            name: 'runtime'
        }
    }
    複製代碼

3. 生產環境 production

生產環境下簡化配置,默認開啓插件 UglifyJsPlugingit

// 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()
- ]
}
複製代碼

固然,生產環境依然能夠經過配置關閉 UglifyJsPlugingithub

optimization: {
    minimize: false
}
複製代碼

4. 開發環境 development

  1. 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.jsonscripts 也須要修改,不然會報以下錯誤:
ERROR in Entry module not found: Error: Can't resolve './src' in 'E:\workspace'
正確修改配置:web

// package.json
{
    "scripts": {
-        "dev": "node build/dev-server.js",
+        "dev": "webpack-dev-server --config build/webpack.dev.config.js"        
    }
}
複製代碼

5. 插件安裝/升級

若是小夥伴正在使用如下插件,請按詳情變動使用,而且可能出現報錯解決:shell

  1. vue-loader v15
  2. thread-loader
  3. eslint-loader v5
  4. copy-webpack-plugin
  5. html-webpack-plugin v4
  6. mini-csss-extract-plugin 替代 extract-text-webpack-plugin
  7. script-text-html-webpack-plugin 廢棄

詳細:

  1. vue-loader v15
    從 v14 遷移npm

    // webpack.base.config.js
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    
    module.exports = {
      // ...
        plugins: [
            new VueLoaderPlugin()
        ]
    }
    複製代碼
  2. thread-loader
    原本打算使用 HappyPack ,但是vue-loader@15 不徹底支持 HappyPack
    如今這個Issue已經關閉,因此小夥伴能夠繼續嘗試用 HappyPack 進行優化,記得反饋哦。

  3. 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
    -   }
    }
    複製代碼
  4. copy-webpack-plugin
    若有報錯以下: TypeError: compilation.contextDependencies.push is not a function 升級插件

    npm i copy-webpack-plugin@latest -D
    複製代碼
  5. 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
    複製代碼
  6. 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時候產生的,小編也只知其一;不知其二,很差解釋。
    參考:

    1. github.com/webpack-con…
    2. github.com/webpack-con…

    ④ 警告:[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。 參考:github.com/webpack-con…
    解決:

    plugins: [
        new ExtractTextPlugun({
     -       filename: '[name].[contenthash:8].css'
     +       filename: '[md5:contenthash:hex:20]'
        }})
    ]
    複製代碼

    注意,如今extract-text-webpack-plugin已經不更新了,因此最好仍是使用官方推薦的 mini-css-extract-plugin

  7. script-text-html-webpack-plugin
    緣由:插件暫不支持 webpack4,將會在 v2版 實現
    解決:使用preload-webpack-plugin

6. 其餘警告或報錯

  1. You may need an appropriate loader to handle this file type
    緣由:package.json 裏安裝依賴變化了,可是因爲 package-lock.json 存在。
    解決辦法:
    全局配置一勞永逸:

    npm config set package-lock false
    複製代碼

    解決當前項目:
    刪除 package-lock.jsonnode_modules,從新執行命令 npm install

  2. __webpack_hmr 404 not found
    緣由:webpack 配置 entry 數組裏面有 build/dev-client 的配置
    解決:刪除這個配置便可

  3. 部分文件內做用域 this = undefined
    緣由:不明
    解決:this 改成 window

  4. 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

  1. 推薦使用 @babel/preset-env 並按需引入 polyfill
    babel 7: @babel/polyfill
    babel 6: babel-polyfill
  2. 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']
    複製代碼
相關文章
相關標籤/搜索