前端实战——技术篇(四、优化相关): vue2项目webpack3升级到webpack5

386 阅读3分钟

一、webpack5 新特性

在前端打包优化的方案中有一种最为简单直接, 那便是使用更高版本的打包工具, webpack 于 2020.10.10 更新了 webpack5 版本, 至今已有近一年的时间, 恰巧在对一个基于 webpack3 的项目进行性能优化, 便记录一下 webpack3 升级 webpack5 的过程.

关于 webpack5 有哪些具体的变更, 可参考官方文档 webpack5 发布日志

以下为部分更新内容:

  • 功能清除
    • require.includes 废弃
    • 不再为 Node.js 模块引入 polyfill
  • 长期缓存
    • 确定的 chunk、模块ID 和导出名称
  • 开发支持
    • 模块联邦
      • ModuleFederationPlugin 插件
  • 构建优化
    • 嵌套的 tree-shaking
    • 内部模块 tree-shaking
  • 性能优化
    • 持久化缓存
  • 代码生成
    • 支持 es6 代码

二、webpack3 升级 webpack5 的改动

需要注意的是升级到 webpack5 , 你的 node 版本需要 >= 10.13.0

npm 6.13.4

node v12.15.0

升级依赖, 以下为本次升级过程中改动的依赖及其版本

npm install webpack@5.74.0 -D
npm install webpack-cli@4.10.0 -D
npm install webpack-dev-server@4.10.1 -D 
npm install webpack-merge@5.8.0 -D
npm install copy-webpack-plugin@9.1.0 -D
npm install vue-loader@15.10.0 -D
npm install html-webpack-plugin@5.5.0 -D
npm install mini-css-extract-plugin@2.6.1 -D
npm install css-minimizer-webpack-plugin@4.0.0 -D

webpack5 的启动命令稍作修改, 需要变更为 webpac serve

// package.json

// "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"dev": "npx webpack serve --config build/webpack.dev.conf.js --color --progress",

替换 devtool

// devtool: 'cheap-module-eval-source-map',
devtool: 'eval-cheap-module-source-map',

merge 需要从依赖包中解构, 而不是直接导出, 所有引用到的地方都需要修改

// const merge = require('webpack-merge')
const { merge } = require('webpack-merge')

需要将中node属性中的设置改到 resolve 的 fallback 属性中。

// webpack.base.conf.js

module.exports = {
    // node: {...},
    resolve: {
        fallback: {
            // prevent webpack from injecting useless setImmediate polyfill because Vue
            // source contains it (although only uses it if it's native).
            setImmediate: false,
            // prevent webpack from injecting mocks to Node native modules
            // that does not make sense for the client
            dgram: 'empty',
            fs: 'empty',
            net: 'empty',
            tls: 'empty',
            child_process: 'empty'
        },
    }
}

添加 VueLoaderPlugin

// webpack.base.config.js

const { VueLoaderPlugin } = require('vue-loader')  // vue版本2.6.14

module.exports = {
    // ...
    plugins: [
        new VueLoaderPlugin(),
        // ...
    ]
}

webpack5改用配置方式, 插件作用: 在热加载时直接返回更新文件名

// webpack.dev.config.js

// new webpack.NamedModulesPlugin(),
module.exports = {
    optimization: {
        moduleIds: 'named' // webpack5 采用此方式代替 NamedModulesPlugin
    }
}
// webpack.prod.config.js

// new webpack.HashedModuleIdsPlugin(),
module.exports = {
    optimization: {
        moduleIds: 'hashed', // webpack5 采用此方式代替 HashedModuleIdsPlugin
    }
}

CopyWebpackPlugin 的参数格式有所变化, 需要在对应的文件中改动

// webpack.dev.config.js、webpack.prod.config.js

// new CopyWebpackPlugin([
//     {
//         from: path.resolve(__dirname, '../static'),
//         to: config.dev.assetsSubDirectory,
//         ignore: ['.*']
//     }
// ]),
new CopyWebpackPlugin({
    patterns:[{
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory, // 在webpack.prod.config.js 中要改成 config.build.assetsSubDirectory
        globOptions: {            // webpack5 ignore要写在globOptions这里
            ignore: ['.*']
        }
    }]
}),

webpack5 弃用了之前的 css 处理插件, 改用下列插件代替

// utils.js

// const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// utils.js generateLoaders function

return [{loader: MiniCssExtractPlugin.loader}].concat(loaders)
// return ExtractTextPlugin.extract({
//   use: loaders,
//   fallback: 'vue-style-loader'
// })

return [{loader: 'vue-style-loader'}].concat(loaders)
// return ['vue-style-loader'].concat(loaders)
// extract-text-webpack-plugin已被移除,由mini-css-extract-plugin插件替代
// optimize-css-assets-webpack-plugin已过时, 由css-minimizer-webpack-plugin插件代替

// const ExtractTextPlugin = require('extract-text-webpack-plugin') 
const MiniCssExtractPlugin = require("mini-css-extract-plugin") 
// const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')

module.exports = {
    plugins: [
        // 删除ExtractTextPlugin、OptimizeCSSPlugin插件引用
        new MiniCssExtractPlugin(),
    ],
    optimization: {
        minimizer: [
            new CssMinimizerWebpackPlugin(),
        ]
    }
}

devServer 中有部分字段变更, 可参照下述代码修改

// webpack.dev.conf.js

devServer: {
    // clientLogLevel: 'warning', // v4 移动到了client下面 改名logging 'warn'
    // contentBase: false,   // v4移动到了static下面
    // overlay: config.dev.errorOverlay
    //   ? { warnings: false, errors: true }   // v4 移动到了client下面
    //   : false,
    // publicPath: config.dev.assetsPublicPath, // v4 移动到了devMiddleware下面
    // quiet: true, // v4移除了该选项
    // watchOptions: {
    //   poll: config.dev.poll,   // v4移动到了static下面的watch
    // },
    // disableHostCheck: true // v4移除了该选项
    client: {
      logging: 'warn',
      overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,
      progress: true,
    },
    static: false,
    devMiddleware: {
      publicPath: config.dev.assetsPublicPath
    },
    historyApiFallback: {
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    hot: true,
    compress: true,
    host: HOST || config.dev.host,
    port: PORT || config.dev.port,
    open: config.dev.autoOpenBrowser,
    proxy: config.dev.proxyTable,
    allowedHosts: "all"
  }

此外之前通过 new webpack.optimize.CommonsChunkPlugin 拆分代码, 现在通过optimization.splitChunks 实现

HtmlWebpackPlugin 中的 chunksSortMode 现在只有’none’ | ‘auto’ | 'manual’三种配置,目前配置成 auto 即可

三、可能遇到的问题

1、[webpack-dev-middleware] HookWebpackError: Not supported

这个其实是 copy-webpack-plugin 的报错, 原因是该插件的版本与 node 的版本不兼容, 如果你的 node 版本 在 12.20.0 以下, 则应该升级 node 版本或者降低插件的版本到10以下

image.png


// 升级 node 版本
npm install node@latest -g

// 降低 copy-webpack-plugin 版本
npm install copy-webpack-plugin@9 -D

2、“exports is not defined”

将 .babelrc 文件中的 "modules": false, 注释掉即可