webpack原理图解(webpack基础分包大揭秘)
webpack原理图解(webpack基础分包大揭秘)CommonJS对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。 因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。NodeJS诞生之后,它使用CommonJS的模块化规范。从此,js模块化开始快速发展。 它有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global漫画 | 带你领略前端发展史的江湖恩怨情仇 - 掘金模块化的开发方式可以提供代码复用率,方便进行代码的管理。通常来说,一个文件就是一个模块,有自己的作用域,只向外
作者:ninaqqli,PCG用户前端开发工程师
| 导语 若若若你对webpack仅仅是处于使用阶段,觉得webpack原理太杂太乱太多,但是觉得大概了解下webpack的大致原理也不错。 亦或是想要了解分包优化如何进行配置呢?以及为什么webpack官方分包配置会从 CommmonsChunkPlugin演变成SplitChunksPlugin呢? 我也仅仅处于小白阶段,所以按照自己的方式,通过查阅、整理相关文档,梳理一些比较容易让大家纠结的点~让大家通过该篇文章,大概了解webpack是干了啥呢? 该篇从前端模块化、webpack的由来、webpack的核心流程解析、chunk的相关概念以及分包规则、SplitChunksplugin的由来及使用、打包构建工具对比~提供全方面但又比较简单理解的知识点~ 也在每个章节提供了相关的参考文章,供大家参考,希望有帮助!!!
1. webpack前生今世1.1 前端石器时代----->工业化时代前端变迁转折点
- 2008年9月2号,当Chrome第一次出现的时候(V8与Chrome同一天宣布开源),它对网页的加载速度让所有人惊叹,是V8引擎把JavaScript的运行速度提上来了,让前端从蒸汽机机时代正式步入内燃机时代
- 2009年诞生的Node.js和2010年诞生的npm 迅速将JavaScript变成全球最受欢迎的生态系统之一。前端正式从石器时代进入到了工业化时代
强推:司徒大佬:前端开发 20 年变迁史 - 知乎
漫画 | 带你领略前端发展史的江湖恩怨情仇 - 掘金
1.2 前端为什么需要模块化1.2.1 痛点- 变量和方法不容易维护,容易污染全局作用域
- 加载资源的方式通过script标签从上到下
- 依赖的环境主观逻辑偏重,代码较多就会比较复杂
- 大型项目资源难以维护,特别是多人合作的情况下,资源的引入会让人奔溃
模块化的开发方式可以提供代码复用率,方便进行代码的管理。通常来说,一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。 有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
1.2.3 模块规范但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范,这一点就更重要了。 目前流行的js模块化规范有CommonJS、AMD、CMD以及es6的模块系统。
- CommonJS
NodeJS诞生之后,它使用CommonJS的模块化规范。从此,js模块化开始快速发展。 它有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global
- AMD
CommonJS对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。 因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。 主要有两个Javascript库实现了AMD规范:require.js和curl.js
- CMD
CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。 此规范其实是在sea.js推广过程中产生的。
- ES6
在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
1.2.4 模块化总结 1.2.4 参考文章Webpack前世今生 - 说故事的五公子 - 博客园
为什么选择 webpack | webpack 中文文档
GitHub - morrain/webpack-learning: 配套的PPT
前端模块化——彻底搞懂AMD、CMD、ESM和CommonJS - 奔跑的瓜牛 - 博客园
Javascript模块化编程(一):模块的写法 - 阮一峰的网络日志
Javascript模块化编程(二):AMD规范 - 阮一峰的网络日志
Javascript模块化编程(三):require.js的用法 - 阮一峰的网络日志
1.3 为什么需要webpack呢?前端页面效果越来越酷炫、功能越来越复杂。而前端工程师们为了更方便的开发提高开发效率进行了一系列der探索,模块化思想的提出啊 将复杂的程序分割成更小的文件。这些年优秀的框架层出不穷react、vue、angular、es6这种在javascript基础上拓展的新的语法规范和 less、sass、css处理器等等等。 所有的事物都是具有双面性的、有利有弊。大大提高开发效率的同时,又为后期维护造成了困扰。因为利用这些工具的文件往往不能直接被浏览器识别,需要手动处理,很影响开发进度。
是否可以有一种方式,不仅可以让我们编写模块,而且还支持任何模块格式(至少在我们到达 ESM 之前),并且可以同时处理资源和资产? 所以webpack应运而生~这就是 webpack 存在的原因。它是一个工具,可以打包你的 JavaScript 应用程序(支持 ESM 和 CommonJS),可以扩展为支持许多不同的静态资源,例如:images fonts 和 stylesheets。
webpack 关心性能和加载时间;它始终在改进或添加新功能,例如:异步地加载 chunk 和预取,以便为你的项目和用户提供最佳体验。
是时代进步的产物hhh!
2. webpack概念webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
3. webpack核心流程解析总体流程架构图
上述提及的各类技术名词不太熟悉的同学,可以先看看简介:
Entry:编译入口,webpack 编译的起点
Compiler:编译管理器,webpack 启动后会创建 compiler 对象,该对象一直存活直到结束退出
Compilation:单次编辑过程的管理器,比如 watch = true 时,运行过程中只有一个 compiler 但每次文件变更触发重新编译时,都会创建一个新的 compilation 对象
Dependence:依赖对象,webpack 基于该类型记录模块间依赖关系
Module:webpack 内部所有资源都会以“module”对象形式存在,所有关于资源的操作、转译、合并都是以 “module” 为基本单位进行的
Chunk:编译完成准备输出时,webpack 会将 module 按特定的规则组织成一个一个的 chunk,这些 chunk 某种程度上跟最终输出一一对应
Loader:资源内容转换器,其实就是实现从内容 A 转换 B 的转换器
Plugin:webpack构建过程中,会在特定的时机广播对应的事件,插件监听这些事件,在特定时间点介入编译过程
扩展:
Compiler 是plugin的apply接口传进来的参数,它代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用,可以使用它来访问 webpack 的主环境。对于 plugin 而言,通过它来注册事件钩子。
Compilation对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。对于plugin而言,通过它来完成数据的处理。
3.1 初始化阶段初始化参数:从配置文件、 配置对象、Shell 参数中读取,与默认配置结合得出最终的参数 创建编译器对象:用上一步得到的参数创建 Compiler 对象 初始化编译环境:包括注入内置插件、注册各种模块工厂、初始化 RuleSet 集合、加载配 置的插件等 开始编译:执行 compiler 对象的 run 方法 确定入口:根据配置中的 entry 找出所有的入口文件,调用 compilition.addEntry 将入口文件转换为 dependence 对象 这个过程需要在 webpack 初始化的时候预埋下各种插件,经历 4 个文件,7次跳转才开始进入主题。
3.2 构建阶段编译模块(make):根据 entry 对应的 dependence 创建 module 对象,调用 loader 将模块转译为标准 JS 内容,调用 JS 解释器将内容转换为 AST 对象,从中找出该模块依赖的模块,再递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理
完成模块编译:上一步递归处理所有能触达到的模块后,得到了每个模块被翻译后的内容以及它们之间依赖关系图
构建阶段从 entry 开始递归解析资源与资源的依赖,在 compilation 对象内逐步构建出 module 集合以及 module 之间的依赖关系
这个过程中数据流 module => ast => dependences => module ,先转 AST 再从 AST 找依赖。compilation 按这个流程递归处理,逐步解析出每个模块的内容以及 module 依赖关系,后续就可以根据这些内容打包输出
3.3 生成阶段输出资源(seal):根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
写入文件系统(emitAssets):在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
seal的关键逻辑是将 module 按规则组织成 chunks ,webpack 内置的 chunk 封装规则比较简单: entry 及 entry 触达到的模块,组合成一个 chunk 使用动态引入语句引入的模块,各自组合成一个 chunk
chunk 是输出的基本单位,默认情况下这些 chunks 与最终输出的资源一一对应,那按上面的规则大致上可以推导出一个 entry 会对应打包出一个资源,而通过动态引入语句引入的模块,也对应会打包出相应的资源
3.4 参考文章企业级 Webpack 分享总结(内附完整ppt)
GitHub - morrain/webpack-learning: 配套的PPT
强推:[万字总结] 一文吃透 Webpack 核心原理 - 掘金
4. chunk概念及分包基本规则4.1 webpack资源形态流转webpack资源形态流转
从资源流转的层面,我们来看下webpack的打包流程
- compiler.make 阶段:
entry 文件以 dependence 对象形式加入 compilation 的依赖列表,dependence 对象记录有 entry 的类型、路径等信息
根据 dependence 调用对应的工厂函数创建 module 对象,之后读入 module 对应的文件内容,调用 loader-runner 对内容做转化,转化结果若有其它依赖则继续读入依赖资源,重复此过程直到所有依赖均被转化为 module - compilation.seal 阶段:
遍历 module 集合,根据 entry 配置及引入资源的方式,将 module 分配到不同的 chunk
遍历 chunk 集合,调用 compilation.emitAsset 方法标记 chunk 的输出规则,即转化为 assets 集合 - compiler.emitAssets 阶段:
将 assets 写入文件系统
综上,Module 主要作用在 webpack 编译过程的前半段,解决原始资源“如何读”的问题;而 Chunk 对象则主要作用在编译的后半段,解决编译产物“如何写”的问题,两者合作搭建起 webpack 搭建主流程。
从上面的webpack资源形态流转图以及解析中,我们不难发现chunk的大概概念。
chunk:webpack 实现中,原始的资源模块以 Module 对象形式存在、流转、解析处理。
而 Chunk 则是输出产物的基本组织单位,在生成阶段 webpack 按规则将 entry 及其它Module 插入 Chunk 中,之后再由 SplitChunksPlugin 插件根据优化规则与 ChunkGraph 对 Chunk 做一系列的变化、拆解、合并操作,重新组织成一批性能(可能)更高的 Chunks 。运行完毕之后 webpack 继续将 chunk 一一写入物理文件中,完成编译工作。 代码块,是webpack根据功能拆分出来的(chunk是无法在打包结果中看到的,打包结果中看到的是bundle)
4.3 chunk的基本分包规则chunk可以分为三类;
- 每个 entry 项都会对应生成一个 chunk 对象,称之为 initial chunk
- 每个异步模块都会对应生成一个 chunk 对象,称之为 async chunk
- Webpack 5 之后,如果 entry 配置中包含 runtime 值,则在 entry 之外再增加一个专门容纳 runtime 的 chunk 对象,此时可以称之为 runtime chunk
默认情况下 initial chunk 通常包含运行该 entry 所需要的所有 runtime 代码,但 webpack 5 之后出现的第三条规则打破了这一限制,允许开发者将 runtime 从 initial chunk 中剥离出来独立为一个多 entry 间可共享的 runtime chunk
注意: 「业务模块」 是指开发者所编写的项目代码; 「runtime 模块」 是指 Webpack 分析业务模块后,动态注入的用于支撑各项特性的运行时代码
参考文章 有点难的知识点: Webpack Chunk 分包规则详解 - 云 社区 - 腾讯云 Webpack 系列第五篇: 彻底理解 Webpack 运行时 - SegmentFault 思否
4.4 bundle vs chunkbundle: bundle是webpack打包之后的各个文件,一般就是和chunk是一对一的关系,但有时候也不完全是一对一的关系。bundle就是对chunk进行编译压缩打包等处理之后的产出
Chunk是过程中的代码块,Bundle是结果的代码块。
参考文章:
github/webpack/webpack.js/issues/970
webpack中的chunk 和 bundle 有什么区别 ? - SegmentFault 思否
概念术语 | webpack 中文网
深入理解 Webpack 打包分块(上) - 知乎
5. SplitChunksPlugin的前世今生默认情况下,Webpack 会将所有代码构建成一个单独的包,这在小型项目通常不会有明显的性能问题,但伴随着项目的推进,包体积逐步增长可能会导致应用的响应耗时越来越长。归根结底这种将所有资源打包成一个文件的方式存在两个弊端:
「资源冗余」:客户端必须等待整个应用的代码包都加载完毕才能启动运行,但可能用户当下访问的内容只需要使用其中一部分代码
「缓存失效」:将所有资源达成一个包后,所有改动 —— 即使只是修改了一个字符,客户端都需要重新下载整个代码包,缓存命中率极低
一个多页面应用,所有页面都依赖于相同的基础库,那么这些所有页面对应的 entry 都会包含有基础库代码,这岂不浪费?这些问题都可以通过对产物做适当的分解拆包解决 ,诞生了CommonsChunkPlugin
5.1 CommmonsChunkPlugin的弊端CommmonsChunkPlugin的思路是Create this chunk and move all modules matching minChunks into the new chunk,即将满足minChunks配置想所设置的条件的模块移到一个新的chunk文件中去,这个思路是基于父子关系的,也就是这个新产出的new chunk是所有chunk的父亲,在加载孩子chunk的时候,父亲chunk是必须要提前加载的。举例:
- 同步模块加载
example:
entryA: vue vuex someComponents
entryB: vue axios someComponents
entryC: vue vux axios someComponents minchunks: 2
- 产出后的chunk:
vendor-chunk:vue vuex axios
chunkA~chunkC: only the components
对entryA和entryB来说,vendor-chunk都包含了多余的module
- 异步的模块
example:
entryA: vue vuex someComponents
asyncB:vue axios someComponents
entryC: vue vux axios someComponents
minchunks: 2
产出后的chunk:
vendor-chunk:vue vuex
chunkA: only the components
chunkB: vue axios someComponents
chunkC: axios someComponents
带来的问题是:如果asyncB在entryA中动态引入,则会引入多余的module。
总的来说,CommonsChunkPlugin有以下三个问题:
1. 产出的chunk在引入的时候,会包含重复的代码;
2. 无法优化异步chunk;
3. 高优的chunk产出需要的minchunks配置比较复杂。
综上~ CommonsChunkPlugin有很多问题:
- 它可能会导致许多不必要的代码代码被加载
- 它会影响异步加载的chunk
- 它很难使用
- 它使用起来很难理解
针对以上种种问题,webpack4集成了新的插件:SplitChunksPlugin
其中,可以发现SplitChunksPlugin产出的vendor-chunk有多个,对于入口A来说,引入的代码只有chunkA、vendor-chunkA-B、vendor-chunkA-C、vendor-chunkA-B-C;这时候chunkA、vendor-chunkA-B、vendor-chunkA-C、vendor-chunkA-B-C形成了一个chunkGroup。 SplitChunksPlugin它会使用模块重复计数和模块种类(node_modules)自动识别哪些chunk需要被分离。 可以类比一下两个插件。CommonsChunkPlugin就类似于:创建这个chunk并且把所有与minChunks字段匹配的模块移到新的chunk中。而SplitChunksPlugin就类似于:这是启发式的,确保你需要他们(命令式 vs 声明式)
5.3 SplitChunksPlugin的概念和优点SplitChunksPlugin 是 Webpack 4 之后引入的分包方案(此前为 CommonsChunkPlugin),它能够基于一些启发式的规则将 Module 编排进不同的 Chunk 序列,并最终将应用代码分门别类打包出多份产物,从而实现分包功能。
SplitChunksPlugin有一些很赞的属性
- 不会下载不必要的模块(只要你强制使用name属性合并chunk)
- 对异步加载的chunk也有效
- 默认情况下,只对异步加载的chunk有效
- 处理从多个vendor chunks分离出来的vendor
- 更容易使用
- 不需要依赖chunk graph的hack手法
- 更加自动化
命名规则大致举例如下:
5.4 参考文章【译】webpack 4: Code Splitting - 知乎
有点难的 webpack 知识点:Dependency Graph 深度解析
webpack chunk相关分享 - 知乎
6. SplitChunksPlugin使用使用上,SplitChunksPlugin 的配置规则比较抽象,算得上 Webpack 的一个难点,仔细拆解后关键逻辑在于:
- SplitChunksPlugin 通过 module 被引用频率、chunk 大小、包请求数三个维度决定是否执行分包操作,这些决策都可以通过 optimization.splitChunks 配置项调整定制,基于这些维度我们可以实现:
- 单独打包某些特定路径的内容,例如 node_modules 打包为 vendors
- 单独打包使用频率较高的文件
- SplitChunksPlugin 还提供配置组概念 optimization.splitChunks.cacheGroup,用于为不同类型的资源设置更有针对性的配置信息
- SplitChunksPlugin 还内置了 default 与 defaultVendors 两个配置组,提供一些开箱即用的特性:
- node_modules 资源会命中 defaultVendors 规则,并被单独打包
- 只有包体超过 20kb 的 Chunk 才会被单独打包
- 加载 Async Chunk 所需请求数不得超过 30
- 加载 Initial Chunk 所需请求数不得超过 30
SplitChunksPlugin 默认只对 Async Chunk 生效,开发者也可以通过 optimization.splitChunks.chunks 调整作用范围,该配置项支持如下值:
字符串 'all' :对 Initial Chunk 与 Async Chunk 都生效,建议优先使用该值 字符串 'initial' :只对 Initial Chunk 生效 字符串 'async' :只对 Async Chunk 生效 函数 (chunk) => boolean :该函数返回 true 时生效
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
6.2 根据 Module 使用频率分包(minChunks)
module.exports = {
//...
optimization: {
splitChunks: {
// 设定引用次数超过 3 的模块才进行分包
minChunks: 3
}
}
}
SplitChunksPlugin 支持按 Module 被 Chunk 引用的次数决定是否进行分包,开发者可通过 optimization.splitChunks.minChunks 设定最小引用次数
示例中,entry-a、entry-b 分别被视作 Initial Chunk 处理;async-module 被 entry-a 以异步方式引入,因此被视作 Async Chunk 处理。那么对于 common 模块来说,分别被三个不同的 Chunk 引入,此时引用次数为 3,命中 optimization.splitChunks.minChunks = 2 规则,因此该模块「可能」会被单独分包,最终产物: entry-a.js entry-b.js async-module.js common.js
6.3 限制分包数量(maxInitialRequest/maxAsyncRequests)在满足 minChunks 基础上,还可以通过 maxInitialRequest/maxAsyncRequests 配置项限定分包数量,配置项语义: maxInitialRequest:用于设置 Initial Chunk 最大并行请求数 maxAsyncRequests:用于设置 Async Chunk 最大并行请求数
!!!这里所说的“请求数”,是指加载一个 Chunk 时所需同步加载的分包数。例如对于一个 Chunk A,如果根据分包规则(如模块引用次数、第三方包)分离出了若干子 Chunk A¡,那么请求 A 时,浏览器需要同时请求所有的 A¡,此时并行请求数等于 ¡ 个分包加 A 主包,即 ¡ 1。
若 minChunks = 2 ,则 common-1 、common-2 同时命中 minChunks 规则被分别打包,浏览器请求 entry-b 时需要同时请求 common-1 、common-2 两个分包,并行数为 2 1 = 3,此时若 maxInitialRequest = 2,则分包数超过阈值,SplitChunksPlugin 会放弃 common-1 、common-2 中体积较小的分包。maxAsyncRequest 逻辑与此类似,不在赘述。
并行请求数关键逻辑总结如下:
Initial Chunk 本身算一个请求 Async Chunk 不算并行请求 通过 runtimeChunk 拆分出的 runtime 不算并行请求 如果同时有两个 Chunk 满足拆分规则,但是 maxInitialRequests(或 maxAsyncRequest) 的值只能允许再拆分一个模块,那么体积更大的模块会被优先拆解
6.4 限制分包体积(minSize、maxSize.........)在满足 minChunks 与 maxInitialRequests 的基础上,SplitChunksPlugin 还会进一步判断 Chunk 包大小决定是否分包,这一规则相关的配置项非常多:
minSize:超过这个尺寸的 Chunk 才会正式被分包 maxSize:超过这个尺寸的 Chunk 会尝试继续做分包 maxAsyncSize:与 maxSize 功能类似,但只对异步引入的模块生效 maxInitialSize:与 maxSize 类似,但只对 entry 配置的入口模块生效 enforceSizeThreshold:超过这个尺寸的 Chunk 会被强制分包,忽略上述其它 size 限制
那么,结合前面介绍的两种规则,SplitChunksPlugin 的主体流程如下:
- SplitChunksPlugin 尝试将命中 minChunks 规则的 Module 统一抽到一个额外的 Chunk 对象;
- 判断该 Chunk 是否满足 maxInitialRequests 阈值,若满足则进行下一步
- 判断该 Chunk 资源的体积是否大于上述配置项 minSize 声明的下限阈值;
- 如果体积「小于」 minSize 则取消这次分包,对应的 Module 依然会被合并入原来的 Chunk
- 如果 Chunk 体积「大于」 minSize 则判断是否超过 maxSize、maxAsyncSize、maxInitialSize 声明的上限阈值,如果超过则尝试将该 Chunk 继续分割成更小的部分
注意,这些属性的优先级顺序为:
maxInitialRequest/maxAsyncRequests < maxSize < minSize
而命中 enforceSizeThreshold 阈值的 Chunk 会直接跳过这些属性判断,强制进行分包。
6.5 cacheGroups
cacheGroups 配置项用于为不同文件组设置不同的规则,例如: 1、基本规则
module.exports = {
//...
optimization: {
splitChunks: {
minChunks: 2
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/
minChunks: 1
minSize: 0
}
}
}
}
};
示例通过 cacheGroups 属性设置 vendors 缓存组,所有命中 vendors.test 规则的模块都会被视作 vendors 分组,优先应用该组下的 minChunks、minSize 等分包配置。
除了 minChunks 等分包基础配置项之外,cacheGroups 还支持一些与分组逻辑强相关的属性,包括:
test:接受正则表达式、函数及字符串,所有符合 test 判断的 Module 或 Chunk 都会被分到该组 type:接受正则表达式、函数及字符串,与 test 类似均用于筛选分组命中的模块,区别是它判断的依据是文件类型而不是文件名,例如 type = 'json' 会命中所有 JSON 文件 name:chunk命名 priority:数字型,用于设置该分组的优先级,若模块命中多个缓存组,则优先被分到 priority 更大的组 缓存组的作用在于能为不同类型的资源设置更具适用性的分包规则,一个典型场景是将所有 node_modules 下的模块统一打包到 vendors 产物,从而实现第三方库与业务代码的分离。
2、默认分组
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async'
minSize: 30000
maxSize: 0
minChunks: 1
maxAsyncRequests: 5
maxInitialRequests: 3
automaticNameDelimiter: '~'
name: true
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/
priority: -10
}
default: {
minChunks: 2
priority: -20
reuseExistingChunk: true
}
}
}
}
};
这两个配置组能帮助我们:
- 将所有 node_modules 中的资源单独打包到 vendors.js 命名的产物
- 对引用次数大于等于 2 的模块,也就是被多个 Chunk 引用的模块,单独打包
参考文章 Webpack 性能系列四:分包优化
7. SplitChunksPlugin最佳实战那么,如何设置最适合项目情况的分包规则呢?这个问题并没有放诸四海皆准的通用答案,因为软件系统与现实世界的复杂性,决定了很多计算机问题并没有银弹,不过还是有几条可供参考的最佳实践:
- 「尽量将第三方库拆为独立分包」
- 「保持按路由分包,减少首屏资源负载」
- 「尽量保持」 chunks = 'all'