webpack4 SplitChunks实现代码分隔详解


Posted in Javascript onMay 23, 2019

代码均放在 git仓库

Webpack 4给我们带来了一些改变。包括更快的打包速度,引入了SplitChunksPlugin插件来取代(之前版本里的)CommonsChunksPlugin插件。在这篇文章中,你将学习如何分割你的输出代码,从而提升我们应用的性能。

SplitChunks插件( webpack 4.x以前使用CommonsChunkPlugin )允许我们将公共依赖项提取到现有的 entry chunk 或全新的代码块中。

代码分割的理念

首先搞明白: webpack里的代码分割是个什么鬼? 它允许你将一个文件分割成多个文件。如果使用的好,它能大幅提升你的应用的性能。其原因是基于浏览器会缓存你的代码这一事实。每当你对某一文件做点改变,访问你站点的人们就要重新下载它。然而依赖却很少变动。如果你将(这些依赖)分离成单独的文件,访问者就无需多次重复下载它们了。

使用webpack生成一个或多个包含你源代码最终版本的“打包好的文件”(bundles),(概念上我们当作)它们由(一个一个的)chunks组成。

首先 webpack 总共提供了三种办法来实现 Code Splitting,如下:

  • 入口配置:entry 入口使用多个入口文件;
  • 抽取公有代码:使用 SplitChunks 抽取公有代码;
  • 动态加载 :动态加载一些代码。

这里我们姑且只讨论使用 SplitChunks 抽取公有代码。

splitChunks配置

在src目录下创建三个文件pageA.js、pageB.js和pageC.js。代码详情见文章开头git仓库。

// src/pageA.js
var react = require('react');
var reactDom = require('react-dom');
var utility1 = require('../utils/utility1');
var utility2 = require('../utils/utility2');
new Vue();

module.exports = "pageA";
// src/pageB.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageB";
// src/pageC.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageC";

入口文件 && 出口文件

entry: {
 pageA: "./src/pageA", // 引用utility1.js utility2.js
 pageB: "./src/pageB", // 引用utility2.js utility3.js
 pageC: "./src/pageC", // 引用utility2.js utility3.js
},
output: {
 path: path.join(__dirname, "dist"),
 filename: "[name].[hash:8].bundle.js"
},

配置optimization

首先我们配置optimization如下:

optimization: {
 splitChunks: {
  chunks: "all",
 },

执行npm run build打包命令之后,查看dist目录

webpack4 SplitChunks实现代码分隔详解

可以发现,打包出来的除了三个page文件,还存在一个vendors~pageA~pageB~pageC.[hash].bundle.js文件( 此文件中保存了pageA、pageB、pageC和node_modules中共有的size大于30KB的文件 )。事实上这全靠了配置中本身默认固有一个cacheGroups的配置项:

splitChunks: {
 chunks: "all",
 cacheGroups: {
  vendors: {
  test: /[\\/]node_modules[\\/]/, // 匹配node_modules目录下的文件
  priority: -10 // 优先级配置项
  },
  default: {
  minChunks: 2,
  priority: -20, // 优先级配置项
  reuseExistingChunk: true
  }
 }
 }

在默认设置中,会将 node_mudules 文件夹中的模块打包进一个叫 vendors的bundle中,所有引用超过两次的模块分配到 default bundle 中。更可以通过 priority 来设置优先级。

参数说明如下:

  • chunks:表示从哪些chunks里面抽取代码,除了三个可选字符串值 initial、async、all 之外,还可以通过函数来过滤所需的 chunks;
  • minSize:表示抽取出来的文件在压缩前的最小大小,默认为 30000;
  • maxSize:表示抽取出来的文件在压缩前的最大大小,默认为 0,表示不限制最大大小;
  • minChunks:表示被引用次数,默认为1;上述配置commons中minChunks为2,表示将被多次引用的代码抽离成commons。

值得注意的是,如果没有修改minSize属性的话,而且被公用的代码(假设是utilities.js)size小于30KB的话,它就不会分割成一个单独的文件。在真实情形下,这是合理的,因为(如分割)并不能带来性能确实的提升,反而使得浏览器多了一次对utilities.js的请求,而这个utilities.js又是如此之小(不划算)。

  • maxAsyncRequests:最大的按需(异步)加载次数,默认为 5;
  • maxInitialRequests:最大的初始化加载次数,默认为 3;
  • automaticNameDelimiter:抽取出来的文件的自动生成名字的分割符,默认为 ~;
  • name:抽取出来文件的名字,默认为 true,表示自动生成文件名;
  • cacheGroups: 缓存组。(这才是配置的关键)

缓存组会继承splitChunks的配置,但是 test、priorty和reuseExistingChunk只能用于配置缓存组 。cacheGroups是一个对象,按上述介绍的键值对方式来配置即可,值代表对应的选项。除此之外,所有上面列出的选择都是可以用在缓存组里的:chunks, minSize, minChunks, maxAsyncRequests, maxInitialRequests, name。可以通过optimization.splitChunks.cacheGroups.default: false禁用default缓存组。 默认缓存组的优先级(priotity)是负数,因此所有自定义缓存组都可以有比它更高优先级(译注:更高优先级的缓存组可以优先打包所选择的模块)(默认自定义缓存组优先级为0)

现在我们再重新来看一下pageA、pageB、pageC三个js文件,这三个文件中都引入了utility2.js文件,但是此文件size很明显小于30KB,所以这部分公用代码并没有分割出来。如果想要分割出来很简单,只需要:

optimization: {
 splitChunks: {
  chunks: "all",
  minSize: 0
 },

执行npm run build打包命令之后,查看dist目录

webpack4 SplitChunks实现代码分隔详解

显然多了一个pageA~pageB~pageC.[hash].bundle.js文件。查看文件可得知此文件中存储了utility2.js中的代码。如下图所示(借助于webpack-bundle-analyzer插件,详情文章末尾附录)。

webpack4 SplitChunks实现代码分隔详解

上图可以看出,React相关代码均放在了vendors~pageA~pageB~pageC.[hash].bundle.js文件中,如果我们想要抽离出React代码,应该怎么做呐?

splitChunks: {
  chunks: "all",
  cacheGroups: {
  commons: {
   chunks: "initial",
   minChunks: 2,
   name: "commons",
   maxInitialRequests: 5,
   minSize: 0, // 默认是30kb,minSize设置为0之后
       // 多次引用的utility1.js和utility2.js会被压缩到commons中
  },
  reactBase: {
   test: (module) => {
   return /react|redux|prop-types/.test(module.context);
   }, // 直接使用 test 来做路径匹配,抽离react相关代码
   chunks: "initial",
   name: "reactBase",
   priority: 10,
  }
  }
 },

run build之后如下图所示。

webpack4 SplitChunks实现代码分隔详解

看似非常完美,但是reactBase文件中竟然包含了node_modules,神奇的问题?室友都睡觉了,这键盘声影响不好,明天接着看。

附录

我们再安装一个 webpack-bundle-analyzer,这个插件会清晰的展示出打包后的各个bundle所依赖的模块:

npm i webpack-bundle-analyzer -D

引入:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

使用,在plugins数组中添加即可:

new BundleAnalyzerPlugin()

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

Javascript 相关文章推荐
IE6图片加载的一个BUG解决方法
Jul 13 Javascript
再谈Jquery Ajax方法传递到action(补充)
May 12 Javascript
js实现select组件的选择输入过滤代码
Oct 14 Javascript
JavaScript将XML转成JSON的方法
Mar 12 Javascript
10条建议帮助你创建更好的jQuery插件
May 18 Javascript
利用JavaScript阻止表单提交的两种方法
Aug 11 Javascript
JS自定义函数实现时间戳转换成date的方法示例
Aug 27 Javascript
Node中使用ES6语法的基础教程
Jan 05 Javascript
vue 使用Jade模板写html,stylus写css的方法
Feb 23 Javascript
JS解惑之Object中的key是有序的么
May 06 Javascript
详解vue-cli项目开发/生产环境代理实现跨域请求
Jul 23 Javascript
JS实现超级好看的鼠标小尾巴特效
Dec 01 Javascript
微信小程序实现的picker多级联动功能示例
May 23 #Javascript
js console.log打印对象时属性缺失的解决方法
May 23 #Javascript
Node.js+ELK日志规范的实现
May 23 #Javascript
jquery+php后台实现省市区联动功能示例
May 23 #jQuery
js尾调用优化的实现
May 23 #Javascript
浅谈redux, koa, express 中间件实现对比解析
May 23 #Javascript
Express结合Webpack的全栈自动刷新
May 23 #Javascript
You might like
索尼SONY ICF-7600A(W)电路分析
2021/03/01 无线电
防止MySQL注入或HTML表单滥用的PHP程序
2009/01/21 PHP
php中显示数组与对象的实现代码
2011/04/18 PHP
Smarty中常用变量操作符汇总
2014/10/27 PHP
php实现mysql事务处理的方法
2014/12/25 PHP
PHP中in_array的隐式转换的解决方法
2018/03/06 PHP
PHP中检查isset()和!empty()函数的必要性
2019/02/13 PHP
js实现DIV的一些简单控制
2007/06/04 Javascript
JQuery UI皮肤定制
2009/07/27 Javascript
基于jQuery的树控件实现代码(asp.net+json)
2010/07/11 Javascript
jquery中获取id值方法小结
2013/09/22 Javascript
js对table的td进行相同内容合并示例详解
2013/12/27 Javascript
jquery动态更换设置背景图的方法
2014/03/25 Javascript
javascript打开word文档的方法
2014/04/16 Javascript
jquery通过visible来判断标签是否显示或隐藏
2014/05/08 Javascript
javascritp添加url参数将参数加入到url中
2014/09/25 Javascript
在AngularJS应用中实现一些动画效果的代码
2015/06/18 Javascript
js+css实现上下翻页相册代码分享
2015/08/18 Javascript
jQuery+HTML5美女瀑布流布局实现方法
2015/09/21 Javascript
nodejs接入阿里大鱼短信验证码的方法
2017/07/10 NodeJs
在Vue组件中获取全局的点击事件方法
2018/09/06 Javascript
vue插件draggable实现拖拽移动图片顺序
2018/12/01 Javascript
js实现图片粘贴到网页
2019/12/06 Javascript
前端使用crypto.js进行加密的函数代码
2020/08/16 Javascript
[46:23]OG vs EG 2018国际邀请赛淘汰赛BO3 第一场 8.23
2018/08/24 DOTA
python 读取DICOM头文件的实例
2018/05/07 Python
Pycharm 字体大小调整设置的方法实现
2019/09/27 Python
Pycharm内置终端及远程SSH工具的使用教程图文详解
2020/03/19 Python
Python如何绘制日历图和热力图
2020/08/07 Python
CSS3制作彩色进度条样式的代码示例分享
2016/06/23 HTML / CSS
部队领导证婚词
2014/01/12 职场文书
外国人聘用意向书
2014/04/01 职场文书
出国签证在职证明范本
2014/11/24 职场文书
给老婆的保证书
2015/01/16 职场文书
numpy数据类型dtype转换实现
2021/04/24 Python
改造DE1103三步曲
2022/04/07 无线电