详解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简单体验
Jan 10 Javascript
js使用循环清空某个div中的input标签值
Sep 29 Javascript
实例详解jQuery结合GridView控件的使用方法
Jan 04 Javascript
jQuery实现右键菜单、遮罩等效果代码
Sep 27 Javascript
js仿支付宝多方框输入支付密码效果
Sep 27 Javascript
巧用数组制作图片切换js代码
Nov 29 Javascript
100多个基础常用JS函数和语法集合大全
Feb 16 Javascript
javascript标准库(js的标准内置对象)总结
May 26 Javascript
详解vue-cli 3.0 build包太大导致首屏过长的解决方案
Nov 10 Javascript
通过GASP让vue实现动态效果实例代码详解
Nov 24 Javascript
微信小程序如何获取地址
Dec 24 Javascript
django简单的前后端分离的数据传输实例 axios
May 18 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 动态添加记录
2009/03/10 PHP
smarty内置函数{loteral}、{ldelim}和{rdelim}用法实例
2015/01/22 PHP
YiiFramework入门知识点总结(图文教程)
2015/12/28 PHP
php安装扩展mysqli的实现步骤及报错解决办法
2017/09/23 PHP
PHP实现QQ登录的开原理和实现过程
2018/02/04 PHP
Javascript 鼠标移动上去小三角形滑块缓慢跟随效果
2013/04/26 Javascript
深入理解Angularjs中的$resource服务
2016/12/31 Javascript
vue+springboot前后端分离实现单点登录跨域问题解决方法
2018/01/30 Javascript
vue-router配合ElementUI实现导航的实例
2018/02/11 Javascript
使用express获取微信小程序二维码小记
2019/05/21 Javascript
jquery操作select常见方法大全【7种情况】
2019/05/28 jQuery
Vue3.0结合bootstrap创建多页面应用
2019/05/28 Javascript
vue项目启动出现cannot GET /服务错误的解决方法
2020/04/26 Javascript
JavaScript设计模式--桥梁模式引入操作实例分析
2020/05/23 Javascript
Nuxt.js的路由跳转操作(页面跳转nuxt-link)
2020/11/06 Javascript
简单介绍Python下自己编写web框架的一些要点
2015/04/29 Python
示例详解Python3 or Python2 两者之间的差异
2018/08/23 Python
详解Django的CSRF认证实现
2018/10/09 Python
详解Python requests 超时和重试的方法
2018/12/18 Python
Python构建图像分类识别器的方法
2019/01/12 Python
Python玩转PDF的各种骚操作
2019/05/06 Python
Expected conditions模块使用方法汇总代码解析
2020/08/13 Python
HTML5 本地存储和内容按需加载的思路和方法
2011/04/07 HTML / CSS
美国领先的户外服装与装备用品店:Moosejaw
2016/08/25 全球购物
英国航空官网:British Airways
2016/09/11 全球购物
美体小铺瑞典官方网站:The Body Shop瑞典
2018/01/27 全球购物
c/c++某大公司的两道笔试题
2014/02/02 面试题
化工工艺专业求职信
2013/09/22 职场文书
警示教育活动总结
2014/05/05 职场文书
委托书的写法
2014/09/16 职场文书
教师自我剖析材料
2014/09/29 职场文书
2014年保卫工作总结
2014/12/05 职场文书
给老师的保证书怎么写
2015/05/09 职场文书
运动员入场词
2015/07/18 职场文书
2017春节晚会开幕词
2016/03/03 职场文书
MongoDB数据库的安装步骤
2021/06/18 MongoDB