详解webpack4之splitchunksPlugin代码包分拆


Posted in Javascript onDecember 04, 2018

本文讲解的是最近在做的一个多页面项目,结合webpack4的splitChunks进行代码包分拆的过程

项目框架

详解webpack4之splitchunksPlugin代码包分拆

项目有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,
});
...

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,那么每个入口文件就只会打包成为一个文件

优先级关系

maxInitialRequest / maxAsyncRequests <maxSize <minSize。

cacheGroups 定制分割包的规则

test可以配置正则和写入function作为打包规则。其他属性均可继承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下的公共代码。

详解webpack4之splitchunksPlugin代码包分拆

这当然不是我们想要的结果!存在以下问题:

  • 其实当我们进入网站,一般第一步都是进入一个登陆页面,需要的只是项目的基本框架代码,例如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,
  },
  }

详解webpack4之splitchunksPlugin代码包分拆

这次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包内容完全一样,

ok!按照我们的要求,这样首次页面加载只会引入vendors,commons包,而不会引入async-common包,还是挺棒的!追求更精致的我们,再认真想想,是不是还可以做一些更好的优化?

到目前为止我们打包文件的打包是这样的:

详解webpack4之splitchunksPlugin代码包分拆

用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,
  },
  }

详解webpack4之splitchunksPlugin代码包分拆

当然,每次修改后,需要在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左右,当然也不建议拆的特别小,因为浏览器http1可能一次性支持6次下载文件,太多可能会适得其反。大家可以根据自己的项目做不同的拆分方法,总而言之,就是为了让项目更完美的在线上运行,给用户更好的体验~

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

Javascript 相关文章推荐
jQuery select的操作实现代码
May 06 Javascript
table对象中的insertRow与deleteRow使用示例
Jan 26 Javascript
javascript学习笔记(七)Ajax和Http状态码
Oct 08 Javascript
Jquery网页内滑动缓冲导航的实现代码
Apr 05 Javascript
详解JavaScript的回调函数
Nov 20 Javascript
基于jquery实现表格内容筛选功能实例解析
May 09 Javascript
jQuery文字轮播特效
Feb 12 Javascript
Bootstrap模态框插件使用详解
May 11 Javascript
JS图片延迟加载插件LazyImgv1.0用法分析【附demo源码下载】
Sep 04 Javascript
详解js的作用域、预解析机制
Feb 05 Javascript
详解JS浏览器事件循环机制
Mar 27 Javascript
基于Vue 撸一个指令实现拖拽功能
Oct 09 Javascript
React Native中Mobx的使用方法详解
Dec 04 #Javascript
element ui table(表格)实现点击一行展开功能
Dec 04 #Javascript
elementUI中Table表格问题的解决方法
Dec 04 #Javascript
zepto.js 实时监听输入框的方法
Dec 04 #Javascript
vue 之 css module的使用方法
Dec 04 #Javascript
Vue源码解析之数组变异的实现
Dec 04 #Javascript
小程序指纹验证的实现代码
Dec 04 #Javascript
You might like
利用文件属性结合Session实现在线人数统计
2006/10/09 PHP
PHP中Http协议post请求参数
2015/11/02 PHP
PHP使用内置函数file_put_contents写入文件及追加内容的方法
2015/12/07 PHP
详解Laravel设置多态关系模型别名的方式
2019/10/17 PHP
PHP 面向对象程序设计之类属性与类常量实现方法分析
2020/04/13 PHP
JavaScript setTimeout和setInterval的使用方法 说明
2010/03/25 Javascript
jQuery截取指定长度字符串的实现原理及代码
2014/07/01 Javascript
javascript快速排序算法详解
2014/09/17 Javascript
javascript批量修改文件编码格式的方法
2015/01/27 Javascript
javascript操作Cookie(设置、读取、删除)方法详解
2015/03/18 Javascript
原生js模拟淘宝购物车项目实战
2015/11/18 Javascript
深入解析JavaScript框架Backbone.js中的事件机制
2016/02/14 Javascript
JS正则表达式修饰符中multiline(/m)用法分析
2016/12/27 Javascript
JavaScript中运算符规则和隐式类型转换示例详解
2017/09/06 Javascript
JavaScript 自定义事件之我见
2017/09/25 Javascript
详解浏览器缓存和webpack缓存配置
2018/07/06 Javascript
Node.js log4js日志管理详解
2018/07/31 Javascript
详解JS中统计函数执行次数与执行时间
2018/09/04 Javascript
vue3修改link标签默认icon无效问题详解
2019/10/09 Javascript
[01:38]完美世界DOTA2联赛PWL S3 集锦第四期
2020/12/21 DOTA
Python 3实战爬虫之爬取京东图书的图片详解
2017/10/09 Python
python 处理telnet返回的More,以及get想要的那个参数方法
2019/02/14 Python
10分钟理解CSS3 FlexBox弹性布局
2018/12/20 HTML / CSS
美国二手奢侈品寄售网站:TheRealReal
2016/10/29 全球购物
介绍一下SOA和SOA的基本特征
2016/02/24 面试题
法律专业自我鉴定
2013/10/03 职场文书
会计毕业生自荐信
2013/11/21 职场文书
上班离岗检讨书
2014/01/27 职场文书
军训感想500字
2014/02/20 职场文书
低碳生活倡议书
2014/04/14 职场文书
门前三包责任书
2014/04/15 职场文书
公司合作协议书范本
2014/04/18 职场文书
不忘国耻振兴中华演讲稿
2014/05/14 职场文书
新郎父亲婚礼致辞
2015/07/27 职场文书
Django对接elasticsearch实现全文检索的示例代码
2021/08/02 Python
Golang原生rpc(rpc服务端源码解读)
2022/04/07 Golang