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工具库代码
Mar 29 Javascript
JavaScript快速检测浏览器对CSS3特性的支持情况
Sep 26 Javascript
window.navigate 与 window.location.href 的使用区别介绍
Sep 21 Javascript
原生javaScript实现图片延时加载的方法
Dec 22 Javascript
jQuery+canvas实现的球体平抛及颜色动态变换效果
Jan 28 Javascript
简介AngularJS中$http服务的用法
Feb 06 Javascript
Bootstrap3制作自己的导航栏
May 12 Javascript
javascript中获取class的简单实现
Jul 12 Javascript
利用纯Vue.js构建Bootstrap组件
Nov 03 Javascript
JavaScript解析JSON格式数据的方法示例
Jan 24 Javascript
JavaScript调用模式与this关键字绑定的关系
Apr 21 Javascript
使用webpack搭建react开发环境的方法
May 15 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
laravel 4安装及入门图文教程
2014/10/29 PHP
ThinkPHP通过AJAX返回JSON的两种实现方法
2014/12/18 PHP
又十个超级有用的PHP代码片段
2015/09/24 PHP
日常整理PHP中简单的图形处理(经典)
2015/10/26 PHP
PHP实现的oracle分页函数实例
2016/01/25 PHP
基于php伪静态的实现方法解析
2020/07/31 PHP
HR vs ForZe BO3 第二场 2.13
2021/03/10 DOTA
基于jquery的获取mouse坐标插件的实现代码
2010/04/01 Javascript
JavaScript中的prototype和constructor简明总结
2014/04/05 Javascript
JavaScript中获取鼠标位置相关属性总结
2014/10/11 Javascript
JQuery中Text方法用法实例分析
2015/05/18 Javascript
基于jQuery日历插件制作日历
2016/03/11 Javascript
在线引用最新jquery文件的实现方法
2016/08/26 Javascript
利用Angular.js限制textarea输入的字数
2016/10/20 Javascript
利用fecha进行JS日期处理
2016/11/21 Javascript
jQuery实现字符串全部替换的方法
2016/12/12 Javascript
js中toString()和String()区别详解
2017/03/23 Javascript
JavaScript+Html5实现按钮复制文字到剪切板功能(手机网页兼容)
2017/03/30 Javascript
JavaScript模块化之使用requireJS按需加载
2017/04/12 Javascript
bootstrap弹出层的多种触发方式
2017/05/10 Javascript
vue2.0项目集成Cesium的实现方法
2019/07/30 Javascript
js数据类型转换与流程控制操作实例分析
2019/12/18 Javascript
Python3中详解fabfile的编写
2018/06/24 Python
Python数据类型之Dict字典实例详解
2019/05/07 Python
Python多进程multiprocessing、进程池用法实例分析
2020/03/24 Python
关于python的缩进规则的知识点详解
2020/06/22 Python
俄罗斯第一家篮球店:StreetBall
2020/07/30 全球购物
如何让Java程序执行效率更高
2014/06/25 面试题
品质保证书格式
2015/02/28 职场文书
民间借贷借条范本
2015/05/25 职场文书
第一军规观后感
2015/06/12 职场文书
小学语文继续教育研修日志
2015/11/13 职场文书
2016消防宣传标语口号
2015/12/26 职场文书
2020年基层司法所建设情况调研报告
2019/11/30 职场文书
详解Js模块化的作用原理和方案
2021/04/29 Javascript
JS setTimeout与setInterval的区别
2022/04/20 Javascript