分享好友 最新动态首页 最新动态分类 切换频道
深入理解webpack的chunkId对线上缓存的思考
2024-12-29 21:31

想必经常使用基于webpack打包工具的框架的同学们,无论是使用React还是Vue在性能优化上使用最多的应该是分包策略(按需加载)。按需加载的方式使我们的每一个bundle变的更小,在每一个单页中只需要引入当前页所使用到的JavaScript代码,从而提高了代码的加载速度。但是,由于webpack目前版本自身的原因分包策略虽然可以提高我们的加载速度,然而在线上缓存方面却给了我们极大的破坏(webpack5中解决了这个问题)。

深入理解webpack的chunkId对线上缓存的思考

本文主要通过以下四个方面,来深入剖析chunkId:

  • chunkId是怎么生成的?
  • chunkId是怎么破坏线上缓存的?
  • 解决chunkId对破坏缓存的方法
  • 远观未来,webpack5完美解决

webpack是一个基于模块化的打包工具,其总体打包流程可分为:

  1. 初始化阶段
  2. 编译阶段
  3. 输出阶段

初始化阶段

webpack初始化阶段主要在webpack.js中完成,有以下方面:

  • webpack-cli启动,获取webpack.config.js配置及合并shell参数
  • 根据cli得到的配置合并默认配置
  • 创建compiler实例
  • 遍历配置中的plugins加载第三方插件
  • 初始化默认插件

编译阶段

初始化完成之后,cli得到compiler实例,执行compiler.run()开始编译,编译的过程主要分为以下步骤:

  • 分析entry,逐一遍历
  • 确定依赖模块,递归解析
  • 分包策略确定每一个chunk所包含的module,合并module生成chunk。
  • 确定模块资源
  • 根据chunk的entry的不同,确定输出template模板。

输出阶段

  • 输出文件

上面简单了解一下打包流程,当然最主要的目的不是为了了解打包流程,而是其中的一个点:chunk是怎么生成的

从编译阶段中可以看出,chunk是由多个module合并生成的,每一个chunk生成的时候都会有一个对应的chunkId,chunkId的生成策略是本节讨论的重点。

chunkId的生成策略可以在官网中找到,主要有五种规则:

  1. false:不适用任何算法,通过插件提供自定义算法。
  2. natural:自然数ID
  3. named:使用name值作为Id,可读性高。
  4. size:数字ID,依据最小的初始下载大小。
  5. total-size:数字ID,依据最小的总下载大小。

不同的生成规则所打包出来的chunkId是不同的。但是,其实内部生成方式是一样的,不同的规则只是对chunks中的chunk排序规则不同(说的什么玩意,什么一会相同,一会不同的)。不要着急,接下来就来看一下这东西到底是怎么生成的。

我们都知道webpack的optimization中有个chunkIds的配置,上面五种值,就是它的可选值。在开发环境下默认值为named,在生产环境下默认值为size。

在webpack初始化阶段会挂载内部插件,我们直接定位到这个文件的第437行。


上面代码中可以看到,在初始化阶段不同的chunkIds的值会加载不同的插件,并且进入这个插件内部你会发现他们都是挂载到这个钩子上。那么疑问来了,这个钩子是在什么时机执行的呢?定位到``compilation.js`的第1334行会得到答案。


在执行流程中可以看出,chunkId在生成前确定生成规则。可能你的疑问又来了,它是怎么根据chunkId的值的不同生成规则呢?其实所有的chunk都存放在一个数组里面(也就是chunks),在中根据规则的不同对chunk进行相应的排序,然后再统一的对进行赋值。眼见为实,我们先来看一下applyChunkIds中是怎么赋值的,定位到compilation.js中的1754行。


这生成过程中判断chunk.id是否为null,如果为null,对id赋值nextFreeChunkId。没错,无论是什么生成规则,都是这样赋值的。明白了所有的生成规则都是使用相同的赋值规则之后,我们现在的疑问应该就是每个规则中是怎么对chunks进行排序的?接下来就来看一下每个规则是怎么做的。

在中我们可以知道,chunkIds值为natural的时候,挂载的是这个插件。


首先,在每一个chunk中都有一个这个属性,它是一个,里面存放的是所有合并当前的module,每个module的id属性表示当前module的。主要做的事就是根据moduleId来最为排序规则进行排序。

named的生成规则比较简单,根据chunk的name取值


named与其他方式的区别在于,named不是在中对chunkId操作,而是在beforeChunkIds阶段。所做的事是遍历所有的chunk,判断chunk的id值是否为null,如果为null,取到chunk的name值赋予id。

当执行的时候,由于当前的id值已经不是null了,所以跳过赋值规则,直接使用已存在的值。

size和total-size规则由于调用的是相同的插件,只是参数的不同,所以我们就一起看一下它是怎么做的。打开文件。

size和total-size调用插件的区别:

  1. size规则:prioritiseInitial为true。
  2. total-size规则:prioritiseInitial为false。

OccurrenceChunkOrderPlugin通过prioritiseInitial区分是size还是total-size:

  • prioritiseInitial为true:根据父模块的数量排序,如果数量相同走total-size的逻辑。
  • prioritiseInitial为false:首先根据chunk的groups的数量排序,如果数量相同,根据chunk所在的索引排序。
  1. 首先我们会发现除了named之外的规则都是生成的number值,并且只是在生成chunkId前,对chunks以不同的规则进行排序。
  2. 通过named规则,我们可以发现,如果在beforeChunkIds中给chunkId赋值,那么就会阻截默认的规则。

说到破坏,我们心中可能又会有疑问,这东西怎么会破坏线上缓存呢?我们来模拟一个场景。

想必业务思想很好的你,有时候也会让业务的快速变更搞的非常烦恼,假设一个blog项目三个功能模块:文章列表页、文章标签页、关于页,并且三个功能模块都是异步的。我们来简写一下代码。

首先入口文件为index.js,三个功能模块代码为articleList.js、articleTag.js、about.js。


在index.js中异步引入这三个功能模块。


我们使用生产环境打包一下,得到dist目录中的文件如下:

很完美,打包成功,结果也肯定和我们想的一样。

假如有一天,需求变了,关于我们页不想要了,让它暂时不存在项目里面了(为了方便文件的diff,我们先把当前的代码做一个备份),我们可以先把About的代码在index.js中的代码注释。


注释之后,重新打包。重新生成的文件和备份的如下

可以你会说,这不小意思吗?我有webpack的魔法注释,不让文件名变不就得了。(此时作者只能呵呵一笑)我们来验证一下。

我们来把index中引入的三个模块都加上魔法注释:


打包结果如下

  1. 按需加载可以使单个js文件的代码量更小、加载更快,但是带来优化的同时也对缓存产生了极大的伤害。
  2. 缓存是性能优化中极为重要的部分,罪魁祸首在chunkId,所以必须盘它。

相信上述问题,早已被社区的同学们发现,笔者在也曾找了一会插件,但都没有如愿,心里不服,干脆自己写一个。

webpack-fixed-chunk-id-plugin 这个插件已经被笔者发布到npm,代码极简,可能会存在不足,还望社区大佬多多提建议,共同成长。

根据上文我们可以得出,万物的罪魁祸首是chunkId,所以必须要固定它,才能让文件内容不会变。那如何固定呢?

第一点:根据上文第一部分分析chunkId生成原理的时候,我们从named这个规则中得出只要在,这个地方给chunkId一个值,在阶段就不会对chunkId执行定义的规则。

第二点:上一点得出在webpack什么阶段来控制chunkId,那么这点就应该讨论控制chunkId要基于什么来控制? 第一个想到的肯定是内容,基于内容来控制chunkId,当内容变chunkId变、内容不变chunkId不变。

基于上面两点,插件代码如下:


通过挂载到beforeChunkIds钩子上,拿到所有的chunk,遍历每一个chunk得到所有合并当前chunk的module的内容,使用node的crypto加密模块,对内容计算hash值,设置。下面我们来测试一下,这个插件好不好用。





  1. 根据打包问题,确定事故发生地点---chunkId。
  2. 根据事故发生时机,找出阻截事故发生方案---beforeChunkIds。
  3. 定制可行方案---基于module内容来生成唯一hash。

chunkId事故问题可谓webpack自身留下的坑,chunkId方便了开发者,同样chunkId也对我们造成了极大的破坏,正所谓:成也chunkId、败也chunkId。

webpack4中遗留的问题,在还未现世的webpack5中得到了完美的解决。

接下来开始尝鲜webpack5。由于webpack5还未发版,我们可以通过一些方法来使用它。


把webpack4中的src下的代码拷贝到webpack5中打包,结果如下:

我们来按照之前的方式验证一下,把about模块注释,并使用Beyond Compare比较一下。

虽然webpack5可以执行以上操作,但是由于目前还未发布,以cli的配合并不完善。目前的版本,只要写webpack.config.js使用cli启动就会报错,如果要使用配置文件的话就只能使用node来启动webpack。

并且如果要使用webpack5完美的chunkId,还需要在webpack配置文件中配置一下内容:



目前的webpack5已经有了很多优秀的特性,包括代码也变的更加简介,总之,拥抱webpack5吧。

最新文章
AI加持、骗术升级!越南黑客正在横扫亚洲窃取你的财务数据
Cisco Talos研究团队4月4日发布的博文称,他们发现了一个新的威胁行为者,将其命名为“CoralRaider”,Talos认为该威胁行为者源自越南并具有经济动机。CoralRaider至少自2023 年以来一直在运营,目标是多个亚洲和东南亚国家的受害者。该组
Gacha加查nox
Gacha加查nox是一款换装游戏,玩家在其中要展开更欢快的挑战,拥有全新的模组,玩家可以收集的服饰也会更多,只要花费一定的时间,你就能得到更多的成就感。注:游戏界面右下角可以设置中文。人设是什么?《加查俱乐部》又被称为“扭蛋俱乐
AI智能代码助手 Github Copilot Labs
Copilot是GitHub 和 OpenAI 合作开发的一款人工智能代码助手,它可以根据用户输入的注释和代码片段,自动生成高质量的代码。Copilot使用了OpenAI的GPT模型,可以学习和理解大量的代码库和文档,从而生成符合用户需求的代码。Copilot可以与
AI智能创作大师下载
欢迎使用我们的人工智能软件!我们的应用基于先进的技术,为您提供了丰富多彩的功能,让您的生活更加便捷和高效。我的应用采用先进的机器学习技术,不断学习并优化您的数据,以便为您提供更准确的分析。无论是个人使用还是商业应用,它都可
Baidu Comate智能代码助手入门介绍
近年来,随着人工智能技术的飞速发展,AIGC(人工智能生成内容)和大模型成为了科技圈和互联网行业的热门话题。它们以其强大的数据处理能力、高效的学习机制以及广泛的应用前景,赢得了广泛关注。我们可以在
dynamic Spot官方正版 v1.35最新版
《dynamic Spot官方正版》是在谷歌商店上架的一款官方推出的灵动岛软件,专门为安卓手机用户量身打造,模仿还原了iphone14pro的相关功能,十分强大,有需要的可以下载一试!使用dynamicSpot,您可以在设备上轻松获得iPhone 14 Pro Dynamic
360借条协商还本金怎么还不了,款流程是什么
随着互联网金融的快速发展,借款平台成为了人们解决资金需求的一种便捷方式。而360借条作为国内知名的借款平台之一,为用户提供了方便快捷的借款服务。然而,有时借款人在协商还本金时可能会遇到一些困难,导致无法按时还款。本文将围绕“
eBay标题怎么写?5个eBay产品标题优化技巧
eBay标题的撰写不但要考虑到搜索引擎,而且得兼顾可读性。标题不止是为搜索引擎编写,也是为客户编写。搜索引擎可以搜索到一个拼写正确、关键词完整、但词序排列不符合阅读习惯的标题。然而在listing其他展现一样的情况下,客户可能不会点
Facebook最新大杀器—聊天机器人ManyChat介绍和应用
该文为我们广告谈版块第一篇开版之作:)形象地说,就是教你怎样手把手将Facebook打造成微信服务号。Facebook运营和广告投放一直是站外推广和品牌运营的重要手段,这些外面有很多内容啦,我们不会重复。Deal说今天想从一些新的方向去说,提
相关文章
推荐文章
发表评论
0评