Skip to content

项目升级到webpack5

为什么升级到 webpack5

由于公司的后台项目比较大,构建时间非常久。就想着试试升级到 webpack5 情况下能带来多大的优化。

至于 webpack5 相对 webpack4 有哪些提升,官方也给出了说明:

  • 尝试用持久性缓存来提高构建性能。
  • 尝试用更好的算法和默认值来改进长期缓存。
  • 尝试用更好的 Tree Shaking 和代码生成来改善包大小。
  • 尝试改善与网络平台的兼容性。
  • 尝试在不引入任何破坏性变化的情况下,清理那些在实现 v4 功能时处于奇怪状态的内部结构。
  • 试图通过现在引入突破性的变化来为未来的功能做准备,使其能够尽可能长时间地保持在 v5 版本上

TIP

由于本项目当初是基于 vue-cli 创建的,所以本次升级也是先使用 vue-cli 创建一个基于 webpack5 的模板。
webpack的版本由4.46.0 升级至5.75.0

前提准备

webpack-bundle-analyzer

这是一个可以审查打包后的体积分布的插件,构建运行后可以可视化的看到每个模块打包的依赖内容,进而进行相应的构建包体积优化;

npm i webpack-bundle-analyzer -D
js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

plugins: [
    new BundleAnalyzerPlugin(),
]

speed-measure-webpack-plugin

这是一个打包构建耗时分析插件;

npm i webpack-bundle-analyzer -D
js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

plugins: [
    new BundleAnalyzerPlugin(),
]

TIP

使用时,需要将此插件放在最后。

升级过程遇到的问题

配置全局 scss 变量和 mixin

由于使用了较新版本的 sass-loader ,所以配置项有所改变

js
module.exports = defineConfig({
    css: {
        loaderOptions: {
            sass: {
                additionalData: '@import "@/styles/color.scss";',
            },
        },
    }
})

Module not found: Error: Can't resolve 'crypto'

这是因为缺少一些使用到的 node 模块。

最开始,Webpack 目标是允许在浏览器中运行 Node 模块。但是现在在 Webpack 看来,大多模块就是专门为前端开发的。在 v4 及以前的版本中,对于大多数的 Node 模块会自动添加 polyfill 脚本,polyfill 会加到最终的 bundle 中,其实通常情况下是没有必要的。在 v5 中将停止这一行为。

方法一

根据提示在 webpack.config.js里面配置

js
module.exports = {
    resolve: {
        crypto: require.resolve('crypto-browserify')
    }
}

如果是vue-cli,则是在vue.config.js

js
module.exports = defineConfig({
    configureWebpack: {
        resolve: {
            crypto: require.resolve('crypto-browserify')
        }
    },
})

方法二 使用NodePolyfillPlugin

如果觉得一个个引入很麻烦,也可以使用插件引入
vue.config.js

js
module.exports = defineConfig({
    configureWebpack: {
        plugins: [new NodePolyfillPlugin()]
    },
})

Can't resolve 'fs'

缺少 fs

js
module.exports = defineConfig({
    configureWebpack: {
        externals: {
            fs: require('fs'),
        },
    },
})

scss 不支持 exports

把需要导出变量的 scss 文件 改成 xx.module.scss,然后引入。

import $style from '@/xxx.module.scss'

Using / for division is deprecated and will be removed in Dart Sass 2.0.0.

所有 sass 报错均指向 elment-ui 的主题颜色修改,这是因为 sass 一些高级版本已经不再支持一些语法,所以我们需要降低 sass 的版本。

npm i sass@1.30.0 -D

启动

配置前 首次项目启动时间为 44892ms; 二次项目启动时间为 23221ms

开启缓存

二次启动时间减少到 3121ms

js
module.exports = defineConfig({
    configureWebpack: {
        cache: {
            type: 'filesystem', //默认为memory,也就是存储到内存
            buildDependencies: {
                config: [__filename],
            },
        },
    }
})

thread-loade

这是一个多进程构建打包的 loader,能够极大提高构建的速度,只要将 thread-loader 放在构建耗时较大的 loader 之前,比如 babel-loader 等。

npm i thread-loader -D
js
{
    test: /\.js$/,
    include: path.resolve('src'),
    exclude: /node_modules/,
    use: ['thread-loader', 'babel-loader'],
},

打包

打包优化

splitChunks 分包

分包思路基本就围绕着两点:

  • 缩小体积,减少重复
  • 利于缓存,即不变和变要区分开
  1. 由于本项目所有的 api 都是写在 api 文件下,所以可以对其进行打包成单独模块。避免其重复打包进不同页面。
js
api: {
    name: 'api',
    test: /[\\/]api[\\/]/,
    priority: 7,
},
  1. 本项目的自写工具库 utils 同上,并且由于工具库不常变,更有利于缓存
js
utils: {
    name: 'chunk-utils',
    priority: 7,
    test: /[\\/]utils[\\/]/,
}
  1. 将比较大的库单独打成一个模块,有利于缓存和分解模块,避免node_modules 模块过大

提取 element-Ui

js
elementUI: {
    name: 'chunk-elementUI',
    priority: 20,
    test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
},

提取 echarts

js
echarts: {
    name: 'chunk-echarts',
    priority: 20,
    test: /[\\/]node_modules[\\/]_?echarts(.*)/,
},
  1. 将剩余的 node_modules 单独打成一个模块,有利于缓存
js
libs: {
    name: 'chunk-libs',
    test: /[\\/]node_modules[\\/]/,
    priority: 10,
    chunks: 'initial',
},
  1. 将组件单独打包成一个模块,理由同上
js
commons: {
    name: 'chunk-commons',
    test: /[\\/]layout[\\/]|[\\/]components[\\/]/,
    minChunks: 2,
    priority: 5,
    reuseExistingChunk: true,
},

关闭 Source Map

发现在打包后的文件存在 source Map 文件。一般在生产环境中时不需要 source Map 文件,避免暴露出代码。

js
optimization: {
    devtool: process.env.NODE_ENV === 'production' ? false : 'eval-cheap-module-source-map',
}

也可以使用vue-cli的配置

js
module.exports = defineConfig({
    productionSourceMap: false,
})

大家可以根据自己得需求进行配置。

对比

在 source Map 都关闭的情况下,打包体积对比:
配置前 16.5

配置后 14.8