详解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 ready()的几种实现方法小结
Jun 18 Javascript
js constructor的实际作用分析
Nov 15 Javascript
Javascript的常规数组和关联数组对比小结
May 24 Javascript
js快速排序的实现代码
Dec 08 Javascript
javascript创建含数字字母的随机字符串方法总结
Aug 01 Javascript
jquery-mobile基础属性与用法详解
Nov 23 Javascript
JS实现unicode和UTF-8之间的互相转换互转
Jul 05 Javascript
VUE axios上传图片到七牛的实例代码
Jul 28 Javascript
ES6 更易于继承的类语法的使用
Feb 11 Javascript
微信小程序实现watch监听
Jun 04 Javascript
Vue js with语句原理及用法解析
Sep 03 Javascript
vue 解决mintui弹窗弹起来,底部页面滚动bug问题
Nov 12 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
php数据结构与算法(PHP描述) 查找与二分法查找
2012/06/21 PHP
十大使用PHP框架的理由
2015/09/26 PHP
Yii编程开发常见调用技巧集锦
2016/07/15 PHP
Win7环境下Apache连接MySQL提示连接已重置的解决办法
2017/05/09 PHP
redis+php实现微博(一)注册与登录功能详解
2019/09/23 PHP
laravel框架使用阿里云短信发送消息操作示例
2020/02/15 PHP
Nigma vs Liquid BO3 第二场2.13
2021/03/10 DOTA
仅用[]()+!等符号就足以实现几乎任意Javascript代码
2010/03/01 Javascript
js实现分享到随页面滚动而滑动效果的方法
2015/04/10 Javascript
Bootstrap table分页问题汇总
2016/05/30 Javascript
浅谈toLowerCase和toLocaleLowerCase的区别
2016/08/15 Javascript
AngularJS监听路由的变化示例代码
2016/09/23 Javascript
hovertree插件实现二级树形菜单(简单实用)
2016/12/28 Javascript
js 颜色选择插件
2017/01/23 Javascript
详解微信小程序Radio选中样式切换
2017/07/06 Javascript
小程序实现自定义导航栏适配完美版
2019/04/02 Javascript
解决node.js含有%百分号时发送get请求时浏览器地址自动编码的问题
2019/11/20 Javascript
修改vue源码实现动态路由缓存的方法
2020/01/21 Javascript
JQuery Ajax如何实现注册检测用户名
2020/09/25 jQuery
[36:19]2018DOTA2亚洲邀请赛 小组赛 A组加赛 Newbee vs LGD
2018/04/03 DOTA
python的几种开发工具介绍
2007/03/07 Python
Python简单实现安全开关文件的两种方式
2016/09/19 Python
详解python的数字类型变量与其方法
2016/11/20 Python
Python实例方法、类方法、静态方法的区别与作用详解
2019/03/25 Python
基于Python实现大文件分割和命名脚本过程解析
2019/09/29 Python
python使用opencv resize图像不进行插值的操作
2020/07/05 Python
增大python字体的方法步骤
2020/07/05 Python
Pycharm 解决自动格式化冲突的设置操作
2021/01/15 Python
浅谈HTML5新增和废弃的标签
2019/04/28 HTML / CSS
BIBLOO捷克:购买女装、男装、童装、鞋和配件
2017/01/27 全球购物
英国豪华家具和经典家居饰品购物网站:OKA
2020/06/05 全球购物
为什么需要版本控制
2016/10/28 面试题
自我鉴定的范文
2013/10/03 职场文书
全民创业工作总结
2015/08/13 职场文书
《巫师》是美食游戏?CDPR10月将推出《巫师》官方食谱
2022/04/03 其他游戏
Oracle 11g数据库使用expdp每周进行数据备份并上传到备份服务器
2022/06/28 Oracle