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 相关文章推荐
css把超出的部分显示为省略号的方法兼容火狐
Jul 23 Javascript
js各种验证文本框输入格式(正则表达式)
Oct 22 Javascript
客户端限制只能上传jpg格式图片的js代码
Dec 09 Javascript
THREE.JS入门教程(1)THREE.JS使用前了解
Jan 24 Javascript
Javascript常用字符串判断函数代码分享
Dec 08 Javascript
JS简单获取客户端IP地址的方法【调用搜狐接口】
Sep 05 Javascript
Bootstrap源码解读排版(1)
Dec 23 Javascript
Vue实例中生命周期created和mounted的区别详解
Aug 25 Javascript
js解决软键盘遮挡输入框的问题分享
Dec 19 Javascript
JavaScript this绑定过程深入详解
Dec 07 Javascript
JavaScript函数的特性与应用实践深入详解
Dec 30 Javascript
uniapp开发打包多端应用完整方法指南
Dec 24 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
mysql+php分页类(已测)
2008/03/31 PHP
php取出数组单个值的方法
2018/03/12 PHP
解决php写入数据库乱码的问题
2019/09/17 PHP
几个比较实用的JavaScript 测试及效验工具
2010/04/18 Javascript
jquery解析JSON数据示例代码
2014/03/17 Javascript
JavaScript和CSS交互的方法汇总
2014/12/02 Javascript
移动设备web开发首选框架:zeptojs介绍
2015/01/29 Javascript
深入理解JavaScript系列(48):对象创建模式(下篇)
2015/03/04 Javascript
网页收藏夹显示ICO图标(代码少)
2015/08/04 Javascript
Node.js巧妙实现Web应用代码热更新
2015/10/22 Javascript
跟我学习javascript的执行上下文
2015/11/18 Javascript
Bootstrap栅格系统的使用详解
2017/10/30 Javascript
vue-router 源码实现前端路由的两种方式
2018/07/02 Javascript
vue中的router-view组件的使用教程
2018/10/23 Javascript
node.js中ws模块创建服务端和客户端,网页WebSocket客户端
2019/03/06 Javascript
详解VS Code使用之Vue工程配置format代码格式化
2019/03/20 Javascript
JavaScript面试技巧之数组的一些不low操作
2019/03/22 Javascript
小程序自定义模板实现吸顶功能
2020/01/08 Javascript
ElementUI Tree 树形控件的使用并给节点添加图标
2020/02/27 Javascript
微信小程序反编译的实现
2020/12/10 Javascript
Python中使用socket发送HTTP请求数据接收不完整问题解决方法
2015/02/04 Python
Python实现的彩票机选器实例
2015/06/17 Python
Pthon批量处理将pdb文件生成dssp文件
2015/06/21 Python
Python对excel文档的操作方法详解
2018/12/10 Python
Django admin 实现search_fields精确查询实例
2020/03/30 Python
python 绘制场景热力图的示例
2020/09/23 Python
台湾团购、宅配和优惠券:17Life
2017/08/14 全球购物
ManoMano英国:欧洲第一家专注于DIY和园艺市场的电商平台
2020/03/12 全球购物
JDBC操作数据库的基本流程是什么
2014/10/28 面试题
放弃遗产继承公证书
2015/01/26 职场文书
党员干部廉洁自律承诺书
2015/04/28 职场文书
2015年团委副书记工作总结
2015/07/23 职场文书
高中同学会致辞
2015/08/01 职场文书
超级实用的公文标题大全!
2019/07/19 职场文书
2020优秀员工演讲稿(三篇)
2019/10/17 职场文书
Go 在 MongoDB 中常用查询与修改的操作
2021/05/07 Golang