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 相关文章推荐
动态加载图片路径 保持JavaScript控件的相对独立性
Sep 03 Javascript
jQuery用unbind方法去掉hover事件及其他方法介绍
Mar 18 Javascript
js模仿windows桌面图标排列算法具体实现(附图)
Jun 16 Javascript
jQuery中:last-child选择器用法实例
Dec 31 Javascript
JavaScript 事件对象介绍
Apr 13 Javascript
JS获取浮动(float)元素的style.left值为空的快速解决办法
Feb 19 Javascript
通过源码分析Vue的双向数据绑定详解
Sep 24 Javascript
vue2中的keep-alive使用总结及注意事项
Dec 21 Javascript
跨域解决之JSONP和CORS的详细介绍
Nov 21 Javascript
jQuery选择器之基本选择器用法实例分析
Feb 19 jQuery
在Vue.js中使用TypeScript的方法
Mar 19 Javascript
详解vue中v-model和v-bind绑定数据的异同
Aug 10 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
php的mkdir()函数创建文件夹比较安全的权限设置方法
2014/07/28 PHP
php查询whois信息的方法
2015/06/08 PHP
php+jQuery+Ajax简单实现页面异步刷新
2016/08/08 PHP
PHP实现 APP端微信支付功能
2018/06/22 PHP
SwfUpload在IE10上不出现上传按钮的解决方法
2013/06/25 Javascript
web网页按比例显示图片实现原理及js代码
2013/08/09 Javascript
js中各种类型的变量在if条件中是true还是false
2014/07/16 Javascript
jquery提示效果实例分析
2014/11/25 Javascript
javascript实现uploadify上传格式以及个数限制
2015/11/23 Javascript
微信小程序 五星评分的实现实例
2017/08/04 Javascript
解决微信二次分享不显示摘要和图片的问题
2017/08/18 Javascript
JavaScript中Require调用js的实例分享
2017/10/27 Javascript
vue中如何创建多个ueditor实例教程
2017/11/14 Javascript
在Vue中使用Compass的方法
2018/03/02 Javascript
JavaScript实现的鼠标跟随特效示例【2则实例】
2018/12/22 Javascript
JavaScript学习笔记之DOM基础操作实例小结
2019/01/09 Javascript
js实现开关灯效果
2020/03/30 Javascript
微信小程序实现抖音播放效果的实例代码
2020/04/11 Javascript
在项目vue中使用echarts的操作步骤
2020/09/07 Javascript
Nuxt.js的路由跳转操作(页面跳转nuxt-link)
2020/11/06 Javascript
[55:03]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第二场 11.20
2020/11/20 DOTA
python+selenium识别验证码并登录的示例代码
2017/12/21 Python
python实现数据预处理之填充缺失值的示例
2017/12/22 Python
浅谈Tensorflow由于版本问题出现的几种错误及解决方法
2018/06/13 Python
python 实现调用子文件下的模块方法
2018/12/07 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
python实现PDF中表格转化为Excel的方法
2020/06/16 Python
浅谈keras使用预训练模型vgg16分类,损失和准确度不变
2020/07/02 Python
Html5元素及基本语法详解
2016/08/02 HTML / CSS
iframe在移动端的缩放的示例代码
2018/10/12 HTML / CSS
html5 canvas实现给图片添加平铺水印
2019/08/20 HTML / CSS
超市开店计划书
2014/04/26 职场文书
大学生党校培训心得体会
2014/09/11 职场文书
党员个人查摆剖析材料
2014/10/16 职场文书
幼儿学前班评语
2014/12/29 职场文书
python基础之停用词过滤详解
2021/04/21 Python