详解webpack4之splitchunksPlugin代码包分拆

作者:极速慢跑时间:2026-10-22 19:25:36浏览:449

本文详细解析webpack4中splitChunksPlugin的配置方法,通过多页面项目实例演示如何分离公共代码、异步组件及第三方库。重点讲解chunks、minSize、priority等参数对打包结果的影响,并通过优化echarts和d3等大体积库的加载策略,显著降低首屏资源体积,提升页面加载性能。

项目背景与框架介绍

本文介绍了一个多页面项目的构建过程,重点在于利用webpack4的splitChunks插件进行代码包的拆分与优化。

项目框架

项目背景与框架介绍

该项目包含home和topic两个入口文件,主要组成部分如下:

  • 项目基础框架由react、mobx和antd构成。
  • echarts和d3是项目中部分页面使用的大型图表组件库。
  • src目录下的业务组件和代码。
  • 其他非公共代码。

两个入口文件均使用react-loadable实现了异步加载功能。


import Loadable from 'react-loadable';
...
const LoadableLogin = Loadable({
 loader: () => import('../../common/components/login'),
 loading: Loading,
});
...

splitChunks核心配置解析

webpack的相关配置如下所示:


module.exports = {
 ...
 mode: 'production',
 entry: { // 多入口
 home: './src/home',
 topic: './src/topic',
 },
 output: {
 path: config.build.assetsRoot,
 filename: '[name].js',
 publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
 },
 plugins: [
 new HtmlWebpackPlugin({ // home页面
  filename: 'home.html',
  template: './template.html',
 }),
 new HtmlWebpackPlugin({ // topic页面
  filename: 'topic.html',
  template: './template.html',
  inject: true,
 }),
 ],
 ...
}

splitChunks

chunks配置选项

  • all:无论文件是动态还是静态加载,统一进行分离。页面首次加载时会引入所有生成的包。
  • async:仅分离异步加载的文件,首次加载通常不包含,仅在需要异步引入组件时才加载。
  • initial:分离异步和非异步文件。如果一个文件既被异步引入又被非异步引入,它会被打包两次(注意与all的区别),主要用于分离页面首次加载所需的包。

minSize:文件最小打包体积,单位为byte,默认值为30000。

例如,若项目中有三个入口文件a.js、b.js和c.js,大小均为100byte,当minSize设置为301时,webpack会将它们合并为一个包,而不会拆分为多个包。

例如,若项目中有三个入口文件a.js、b.js和c.js,大小均为100byte,当minSize设置为301时,webpack会将它们合并为一个包,而不会拆分为多个包。

automaticNameDelimiter:用于生成文件名时的连接符。

假设生成了一个名为vendor的公共文件,a.js和b.js都依赖它,且连接符设置为"~",最终生成的文件名将是vendor~a~b.js。

maxInitialRequests:入口点处的最大并行请求数,默认值为3。

如果将其设置为1,每个入口文件将只打包成一个文件。

maxAsyncRequests:最大异步请求数量,默认值为5。

如果将其设置为1,每个入口文件将只打包成一个文件。

优先级关系

优先级顺序为:maxInitialRequests / maxAsyncRequests < maxSize < minSize。

cacheGroups:用于定制分割包的规则。

test可以配置正则表达式或函数作为打包规则。其他属性均可继承splitChunks的配置,这里需要特别说明priority,它用于设置包的打包优先级,非常重要(后续结合实践说明)。

minChunks

定义模块被引入的最少次数。

代码分包实践与优化

实践

我们从一个最简单的配置开始,尝试将公共代码打包出来。


 splitChunks: {
  chunks: 'all', // initial、async和all
  minSize: 30000, // 形成一个新代码块最小的体积
  maxAsyncRequests: 5, // 按需加载时候最大的并行请求数
  maxInitialRequests: 3, // 最大初始化请求数
  automaticNameDelimiter: '~', // 打包分割符
  name: true,
  cacheGroups: {
  vendors: { // 打包两个页面的公共代码
   minChunks: 2, // 引入两次及以上被打包
   name: 'vendors', // 分离包的名字
   chunks: 'all'
  },
  }
 },

两个入口文件的公共代码被打包到vendor文件夹下,包括echarts、d3、amcharts等第三方包以及src下的公共代码。

项目背景与框架介绍

这显然不是我们期望的结果,存在以下问题:

  • 通常用户进入网站的第一步是访问登录页面,此时只需要项目的基本框架代码,如react、react-dom、antd等。我们可以使用all(或initial)将它们单独打包,作为首页必须加载的包。
  • 在首次加载页面时,我们只希望加载同步代码,因此需要一个同步的Common包。
  • 对于echarts、d3以及src下部分异步加载的包,可以利用async将它们打包成一个独立的异步加载包。

我们修改cacheGroups配置如下:


 cacheGroups: {
  vendors: { // 项目基本框架等
   chunks: 'all',
   test: /(react|react-dom|react-dom-router|babel-polyfill|mobx)/,
   priority: 100,
   name: 'vendors',
  },
  'async-commons': { // 异步加载公共包、组件等
   chunks: 'async',
   minChunks: 2,
   name: 'async-commons',
   priority: 90,
  },
  commons: { // 其他同步加载公共包
   chunks: 'all',
   minChunks: 2,
   name: 'commons',
   priority: 80,
  },
  }

项目背景与框架介绍

此次webpack生成的包主要包括:

  • async-common:两个入口文件都异步加载的三方包以及利用react-loader实现的懒加载代码,包含echarts、d3等。
  • vendors:包括react、react-dom、antd等基础框架库。
  • commons:引用超过两次的同步代码。

这里有两个需要注意的地方:

  • 注意priority的设置,顺序为vendors > async-commons > commons。我们优先打包react、react-dom等库,然后再打包公共部分。如果将vendors的优先级设置低于两个Common包的优先级,react和react-dom将被打包到common包中,而不会生成独立的vendors包。
  • 如果去掉commons的配置,将会生成一个名为topic~home的包。在配置了async-common提取异步加载公共包后,默认会将同步加载的公共包打包生成以automaticNameDelimiter连接符‘~'命名的‘topic~home'包,其内容与commons包完全相同。

按照我们的要求,这样首次页面加载只会引入vendors和commons包,而不会引入async-common包,效果不错!追求更精致的优化,我们继续思考是否还能做得更好?

到目前为止,我们的打包文件结构如下:

项目背景与框架介绍

经过gzip压缩后,最大的async-common包体积为391kb。由于公司生产环境网速有时仅为20kb/s,导致页面加载缓慢,因此需要继续优化split配置。

分析如下:

  • async-common中包含了自写的src组件和第三方组件。
  • async-common中体积较大的是echarts、zrender(echarts引入)和d3。结合项目实际情况,只有部分页面需要echarts(d3同理),因此可以考虑将d3和echarts这两个大体积包单独提取出来,在特定页面需要时再异步加载,从而大幅减小async-common的体积。

修改配置如下:


cacheGroups: {
  vendors: { // 基本框架
   chunks: 'all',
   test: /(react|react-dom|react-dom-router|babel-polyfill|mobx)/,
   priority: 100,
   name: 'vendors',
  },
  d3Venodr: { // 将体积较大的d3单独提取包,指定页面需要的时候再异步加载
   test: /d3/,
   priority: 100, // 设置高于async-commons,避免打包到async-common中
   name: 'd3Venodr',
   chunks: 'async'
  },
  echartsVenodr: { // 异步加载echarts包
   test: /(echarts|zrender)/,
   priority: 100, // 高于async-commons优先级
   name: 'echartsVenodr',
   chunks: 'async'
  },
  'async-commons': { // 其余异步加载包
   chunks: 'async',
   minChunks: 2,
   name: 'async-commons',
   priority: 90,
  },
  commons: { // 其余同步加载包
   chunks: 'all',
   minChunks: 2,
   name: 'commons',
   priority: 80,
  },
  }

项目背景与框架介绍

每次修改后,需要在htmlWebpackPlugin中配置chunk所需的包。


 plugins: [
 new HtmlWebpackPlugin({ // home页面
  filename: 'home.html',
  template: './template.html',
  chunks: ['vendors', 'commons', 'home'],
 }),
 new HtmlWebpackPlugin({ // topic页面
  filename: 'topic.html',
  template: './template.html',
  chunks: ['vendors', 'commons', 'topic'],
 }),
 ],

后期还进行了其他拆分和优化,大致将最大包体积保持在100k左右。当然,不建议拆分得过于细碎,因为浏览器HTTP/1.1协议可能一次性只支持6个文件下载,过多反而适得其反。大家可以根据自身项目情况采取不同的拆分策略,总之,目的是为了让项目在线上运行更完美,提供更好的用户体验。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

作者最新文章