性能文章>微医对于webpack5持久化缓存优化的实践>

微医对于webpack5持久化缓存优化的实践转载

1月前
184411

导语

 

1、公司的云his静态项目代码量巨大,依赖的npm包大概有100个,打包一次大概要14分钟;

2、自研的hammer工具的本地打包虽然能提升部署时间,但是依赖开发的手动操作;

3、用来存放本地构建产物的服务器容量满了,所以为了正常使用本地打包功能,还得定期去清理服务器上的老文件,不够方便。

 

所以对于webpack的优化势在必行,本文将从webpack5出发详解webpack5的持久化缓存实践。

 

解决思路

1、node版本提升 8.x -> 12.x

微医对于webpack5持久化缓存优化的实践数据图表-heapdump性能社区


2、利用webpack5的持久化缓存提升构建效率速度大幅度提升,快了7倍。

微医对于webpack5持久化缓存优化的实践数据图表-heapdump性能社区

微医对于webpack5持久化缓存优化的实践数据图表-heapdump性能社区


3、使用基于rust开发的swc替代babel,测试的构建速度提升一分钟半左右,因生态不成熟,不能上生产。
关键代码

 

module.exports = {
...
 cache: {
    // 将缓存类型设置为文件系统,默认是memory
    type: 'filesystem',
    buildDependencies: {
      // 更改配置文件时,重新缓存
      config: [__filename]
    }
  },
  optimization: {
    // 值为"single"会创建一个在所有生成chunk之间共享的运行时文件
    runtimeChunk: 'single',
    moduleIds: 'deterministic',
  },
}

webpack 在入口 chunk 中,包含了某些 boilerplate(引导模板),特别是 runtime 和 manifest。这些代码如果不被单独抽离会导致即使没有代码改动,打包出来的文件名仍然会改变,导致无法命中缓存。webpack4中使用HashedModuleIdsPlugin来生成hash值作为模块id,在webpack5中已经不需要了,moduleIds: 'deterministic',是用来保证模块的id不会随着解析顺序的变化而变化,生产环境默认开启。


缓存的方式(从构建层面来讲)

webpack V4

  • cache-loader:建议在开销较大的loader前加,比如babel-loader、vue-loader等;
  • dll:对不经常改变版本的依赖(react、lodash),单独生成动态链接库(bundle),提高构建速度,需要DllPlugin 、DllReferencePlugin 搭配使用,通过引用 dll 的 manifest 文件来把依赖的名称映射到模块的 id 上,之后再在需要的时候通过内置的 __webpack_require__ 函数来 require 他们,推荐在开发模式下使用

webpack V5

  • 文件系统缓存,配置方式见上面的关键代码,作用是将Webpack运行时存在于内存中的那些缓存,不是loader的产物,更不是dll,根据Webpack运行环境的不同,在dev开发时依旧使用MemoryCachePlugin,而在build时使用IdleFileCachePlugin。dev/build的二次编译速度会远超cache-loader

 

一些原理浅谈

Webpack 5 令人期待的持久缓存优化了整个构建流程,原理依然还是那一套:当检测到某个文件变化时,根据依赖关系,只对依赖树上相关的文件进行编译,从而大幅提高了构建速度。官方经过测试,16000 个模块组成的单页应用,速度竟然可以提高 98%!其中值得注意的是持久缓存会将缓存存储到磁盘。


对于一个持续化构建过程来说,第一次构建是一次全量构建,然后它会将相关产物序列化缓存在磁盘中(serialize)。后续构建具体流程可以依赖于上一次的缓存进行:读取磁盘缓存 -> 校验模块 -> 解封模块内容。因为模块之间的关系并不会被显式缓存,因此模块之间的关系仍然需要在每次构建过程中被校验,这个校验过程和正常的 webpack 进行分析依赖关系时的逻辑是完全一致的。


对于 resolver 的缓存同样可以持久化缓存起来,一旦 resolver 缓存经过校验后发现准确匹配,就可以用于快速寻找依赖关系。如果 resolver 缓存校验失败的情况,将会直接执行 resolver 的常规构建逻辑。


缓存的安全性设计


unsafeCache


在webpack 4.x的构建过程中基于timestamp比对策略的一种cache方式,它有两个维度,resolve(解析器)的unsafeCache和module(模块)的unsafeCache。如果同时开启,那么从入口文件开始,webpack通过resolve规则解析所有的依赖文件,将模块之间的依赖关系和解析后的文件内容保存起来,并存储依赖的最后变更时间(timestamp),一旦发现相同引用,返回缓存。


而webpack 5.x版本已经放弃了这种缓存策略,默认只针对开启cache选项并且是node_modules下的依赖才开启unsafeCache,判断是否有文件系统序列化后的文件信息来判断是否需要重新构建。


safeCache


模块间的依赖关系被基于内容对比的算法(contentHash)被记录下来,并存入到ModulGraph的class中的weakmap,相比于依赖时间戳的方式更可靠。


缓存的容量限制


除了需要考虑缓存的安全性,缓存的容量限制也不能忽视,缓存不可能无限叠加,这里就涉及到经典的LRU cache算法(Least Recently Used 最近最少使用)。

微医对于webpack5持久化缓存优化的实践数据图表-heapdump性能社区

微医对于webpack5持久化缓存优化的实践数据图表-heapdump性能社区


单向链表 添加、删除节点O(1),查找O(n)
双向链表加哈希表结合体 O(1)


LRU分析


当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。


LRU算法的改进方案


redis使用的改进算法LIRS、LRU-K等,感兴趣的同学自行查阅

分类:
标签:
请先登录,再评论

👍

3周前

为你推荐

webpack5优化实战
本文围绕webpack优化进行讲述,其中也会作某些原理性地解释,尽量做到让大家知道为什么这么做,以及这么做的结果是什么。
玩转 webpack5(上)
前言本篇长文是学习程柳峰老师开设的《玩转 webpack》专栏的实践笔记,和专栏不一样的是,我的实战源码是基于 webpack5,它的配置和源码实现上与 webpack4 有许多不同的地方,感兴趣的同学可以结合我在上面放出的源码仓库进行学习,相信会有不小的收获。看完本篇长文,你将收获:能
使用 webpack 进行 Web 性能优化(三)
即使您配置 webpack 以使应用程序尽可能小,跟踪它并知道它包含什么仍然很重要。 否则,您可以安装一个使应用程序变大两倍的依赖项——甚至不会注意到它!本节介绍可帮助您了解捆绑包的工具。跟踪包大小要监控您的应用程序大小,请在开发期间使用 webpack-dashboa
使用 webpack 进行 Web 性能优化(二)
改善应用程序加载时间的下一件事(在优化应用程序大小之后)是缓存。 使用它将应用程序的某些部分保留在客户端上,并避免每次都重新下载它们。使用包版本控制和缓存标头进行缓存的常用方法:告诉浏览器将文件缓存很长时间(例如,一年): # Server headerCache-Contro
webpack构建速度和体积优化策略
前言对自己性能优化以及webpack优化进行总结,目前文章对webpack进行了描述,后续会对webpack之前的性能进行总结。整体脑图如下:  初级分析:使用webpack内置的stats在package.json中使用stats "scripts": {
领导:小伙子,咱们这个页面出来太慢了!赶紧给我优化一下!
性能优化这样一个词应该已经是老生常谈了,不仅在面试中面试官会以此和你掰头,而且在工作中领导也会因为网页加载速度慢来敲打你学(打)习(工),那么前端性能优化,如果判断到底需不需要做,如果需要做又怎么去做或者说怎么去找到优化的切入点? 接下来让我们一起来探索前端性能优化(emo~&n
我的第一次webpack优化,首屏渲染从9s到1s
大家好,我是考拉🐨,本文基于vue2(虽然vue3已出,但是本文也很实用)谈到webpack优化大部分人可能都看腻了,无非就那几招嘛,我之前也是看过许多类似的文章,但都没有自己真正上手过,下面是我用公司的项目真实操练下来的,首屏加载速度提升很大(刷刷的),希望能帮到你。废话不多说,先
微医对于webpack5持久化缓存优化的实践
导语 1、公司的云his静态项目代码量巨大,依赖的npm包大概有100个,打包一次大概要14分钟;2、自研的hammer工具的本地打包虽然能提升部署时间,但是依赖开发的手动操作;3、用来存放本地构建产物的服务器容量满了,所以为了正常使用本地打包功能,还得定期去清理服务器上的老文件,