webpack 打包优化 - splitChunks

发布于:2024-04-21 ⋅ 阅读:(43) ⋅ 点赞:(0)

打包时会遇到的问题:

  1. 打包文件过大,首屏加载时间过长,js阻塞页面渲染导致白屏
  2. 改动业务代码后,对于第三方库也会一并重新打包到一个出口文件,浏览器无法利用缓存来减少请求和加载的时间

针对以上两个问题,利用splitChunks可以将各个模块进行分割,并提取出相同部分代码,从而缓解一个js过大、不能缓存第三方库的问题。

webpack 从 4.x 开始支持了零配置的特性,同时对块打包也做了优化,CommonsChunkPlugin已经被移除了,现在是使用optimization.splitChunks代替。(webpack5 splitChunks 配置和 webpack4 大体上一样)

概念区分 - module、bundle、chunk都是什么?

在说SplitChunks前,先来了解几个概念,module、bundle、chunk都是什么?

module:模块,在webpack中任何文件都可以作为一个模块,借用官网的图片,左侧的这些类型文件,都可以认为是一个模块,只是需要配置不同的loader,将文件转换成webpack可以支持打包的文件。

chunk:编译完成准备输出时,webpack 将module 按特定规则组成的一个个 chunk

bundle:webpack 处理好chunk文件后,生成运行在浏览器中的代码

SplitChunks 插件是什么呢

简单的来说就是 Webpack 中一个提取或分离代码的插件,主要作用是提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件。

提到前端优化,提取公共代码是必不可少的手段。
在 Webpack 出现前,提取公共代码是人为去处理,而 SplitChunks 插件的作用就是通过配置让 Webpack 去帮你提取公共代码。SplitChunksPlugin 默认配置适用大部分场景。

打包默认情况下只会影响按需加载模块,因为对初始块也进行优化打包会影响 HTML 中的 script 标签数,增加请求数。

默认情况(webpack 的默认配置)下 webpack 会根据下述条件自动进行代码块分割:

  • 共享模块(至少被引用 2次)或者 node_modules 模块
  • 新代码块大于 20kb(min+gziped之前的体积)
  • 按需加载块时的最大并行请求数将低于或等于 30
  • 初始页面加载时的最大并行请求数将低于或等于 30

SplitChunks插件配置选项

  • 同步加载的模块:通过 import xxx 或 require(‘xxx’) 加载的模块。
  • 异步加载的模块:通过 import(‘xxx’) 或 require([‘xxx’],() =>{}) 加载的模块。
  • chunks: 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),chunks 选项,决定要提取哪些模块
    • 默认是 async :只提取异步加载的模块出来打包到一个文件中。
    • initial 模块下只优化初始模块,也就是说 webpack 只会对初始模块做打包优化。如果 xxx 在项目中异步加载了,也同步加载了,那么 xxx 这个模块会被提取两次,分别打包到不同的文件中。
    • all 模块下同时对初始模块和异步模块做打包优化,不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。
  • minSize: 规定被提取的模块在压缩前的大小最小值,单位为字节。表示在压缩前的最小模块大小,默认为 20000
  • minChunks: 表示被引用次数,默认为 1
  • maxAsyncRequests: 按需加载时候最大的并行请求数,默认为 30
  • maxInitialRequests: 一个入口最大的并行请求数,默认为 30
  • enforceSizeThreshold:强制执行拆分的体积阈值
  • cacheGroups: 缓存组。缓存组的属性除上面所有属性外,还有 test, priority, reuseExistingChunk
    • test: 用于控制哪些模块被这个缓存组匹配到
    • priority: 缓存组打包的先后优先级
    • reuseExistingChunk: 如果当前代码块包含的模块已经有了,就不在产生一个新的代码块

实际使用情景:

1. 设置 chunks: initial || all 都可以提取出第三方库。但是它是把所有第三库提取出来,所以我们在只提取 react 和 react-dom 的情况下,需要自定义一个 cacheGroup。

// webpack.config.js
optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      react: {
          name: 'react',
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
      }
    }
  }
}

安装  webpack-bundle-analyzer, 可以查看打包体积情况,在package.json 中,npm run build 的地方写一个 --report 就会在打包文件中生成一个 report.html, 打开可以看到打包体积情况。  "build": "vue-cli-service build --report"

2. 比如发现chunk-vendors.js的大小有点大了,有1.91MB,还是项目初始化时需要加载的 js 文件,大小过大会导致首屏加载时间过长。

要优化一下,两种方法:可以用SplitChunks优化。

例如要把element从chunk-vendors.js提取出来,就在cacheGroups 进行对应配置:

chainWebpack(config) {
        config.when(process.env.NODE_ENV != 'development',
        config => {
            config.optimization.splitChunks({
                chunks: 'all',
                cacheGroups: {
                    // 第三方组件
                    libs: {
                        // 指定chunks名称
                        name: 'chunk-libs',
                        //符合组的要求就给构建venders
                        test: /[\\/]node_modules[\\/]/,
                        //priority:优先级:数字越大优先级越高,因为默认值为0,所以自定义的一般是负数形式,决定cacheGroups中相同条件下每个组执行的优先顺序。
                        priority: 10,
                        // 仅限于最初依赖的第三方
                        chunks: 'all'
                    },
                    elementUI: {
                        // 将elementUI拆分为单个包
                        name: 'chunk-elementUI',
                        // 重量需要大于libs和app,否则将打包到libs或app中
                        priority: 20,
                        // 为了适应cnpm
                        test: /[\\/]node_modules[\\/]_?element-ui(.*)/
                    },
                    //公共组件
                  //   commons: {
                  //       name: 'chunk-commons',
                  //       // can customize your rules
                  //       test: resolve('src/components'),
                  //       minChunks: 3,
                  //       priority: 30,
                  //       //这个的作用是当前的chunk如果包含了从main里面分离出来的模块,则重用这个模块,这样的问题是会影响chunk的名称。
                  //       reuseExistingChunk: true,
                  //       //最大初始化加载次数,一个入口文件可以并行加载的最大文件数量,默认3
                  //       maxInitialRequests: 3,
                  //       //表示在分离前的最小模块大小,默认为0,最小为30000
                  //       minSize: 0
                  //   },
                }
            })
        })

    }

总结:

webpack5 默认情况下只对异步模块做打包优化,额外的打包必须满足 4 个条件(webpack 的默认配置,可修改)。根据需求可调整为只对初始模块做打包优化,或者初始和异步模块一起优化。同时可配置自己的打包规则。

简而言之:chunks 控制打包作用范围,其他控制打包规则。

webpack5 的 splitChunks 功能是比较强大的,不过推荐还是使用默认模式,或者提取一下第三方库。