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实现动态CSS换肤技术的脚本
Jun 29 Javascript
JavaScript的document对象和window对象详解
Dec 30 Javascript
获取css样式表内样式的js函数currentStyle(IE),defaultView(FF)
Feb 14 Javascript
JavaScript中window.open用法实例详解
Apr 15 Javascript
JavaScript中的small()方法使用详解
Jun 08 Javascript
JavaScript计算某一天是星期几的方法
Aug 05 Javascript
jQuery实现鼠标滚动图片延迟加载效果附源码下载
Jun 28 Javascript
jQuery接受后台传递的List的实例详解
Aug 02 jQuery
VueJs监听window.resize方法示例
Jan 17 Javascript
vue实现div拖拽互换位置
Jul 29 Javascript
Vue中的验证登录状态的实现方法
Mar 09 Javascript
微信小程序实现弹幕墙(祝福墙)
Nov 18 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
Cannot modify header information错误解决方法
2008/10/08 PHP
php微信公众平台配置接口开发程序
2016/09/22 PHP
Thinkphp框架中D方法与M方法的区别
2016/12/23 PHP
IE innerHTML,outerHTML所引起的问题
2009/06/04 Javascript
Jquery 复选框取值兼容FF和IE8(测试有效)
2013/10/29 Javascript
jquery在项目中做复选框时遇到的一些问题笔记
2013/11/17 Javascript
javascript Event对象详解及使用示例
2013/11/22 Javascript
JS辨别访问浏览器判断是android还是ios系统
2014/08/19 Javascript
EasyUI中combobox默认值注意事项
2015/03/01 Javascript
jquery插件NProgress.js制作网页加载进度条
2015/06/05 Javascript
jquery实现一个简单的表单验证实例
2016/03/30 Javascript
浅谈js多维数组和hash数组定义和使用
2016/07/27 Javascript
JavaScript制作简单分页插件
2016/09/11 Javascript
vue分类筛选filter方法简单实例
2017/03/30 Javascript
通过js实现压缩图片上传功能
2020/02/25 Javascript
JS函数本身的作用域实例分析
2020/03/16 Javascript
Python多线程编程(五):死锁的形成
2015/04/05 Python
Python中的条件判断语句与循环语句用法小结
2016/03/21 Python
pyqt5简介及安装方法介绍
2018/01/31 Python
Python 数据处理库 pandas 入门教程基本操作
2018/04/19 Python
Python使用docx模块实现刷题功能代码
2020/02/13 Python
Html5写一个简单的俄罗斯方块小游戏
2019/12/03 HTML / CSS
英国排名第一的在线宠物用品商店:Monster Pet Supplies
2018/05/20 全球购物
牦牛毛户外探险服装:Kora
2019/02/08 全球购物
LN-CC日本:高端男装和女装的奢侈时尚目的地
2019/09/01 全球购物
大学生旅游业创业计划书
2014/01/29 职场文书
小学新教师培训方案
2014/02/03 职场文书
最新奶茶店创业计划书范文
2014/02/08 职场文书
测控技术自荐信
2014/06/05 职场文书
行政求职信
2014/07/04 职场文书
银行员工犯错检讨书
2014/09/16 职场文书
向国旗敬礼活动总结范文2014
2014/09/27 职场文书
2014年就业工作总结
2014/11/26 职场文书
电影雨中的树观后感
2015/06/15 职场文书
Pytorch中的数据集划分&正则化方法
2021/05/27 Python
MySQL中一条SQL查询语句是如何执行的
2022/04/08 MySQL