背景公司cloud他的静态项目代码量巨大,大约有100个npm包依赖。一次打包大约需要14分钟。自研锤子工具的本地打包虽然可以提升部署时间,但是依赖于开发的手工操作中用于存放本地构建产品的服务器容量是否满,所以为了正常使用本地打包功能,需要定期清理服务器上的旧文件,解决问题不够方便。解决方案是升级node版本8.x->12.x使用webpack5的持久化缓存,提高构建效率,速度大大提升,快了7倍。使用基于rust而不是babel开发的swc,测试构建速度提升了大约一分半钟,由于生态不成熟无法投入生产。关键代码module.exports={...cache:{//设置缓存类型为文件系统,默认为内存类型:'filesystem',buildDependencies:{//当更改配置文件时,重新缓存config:[__filename]}},optimization:{//"single"的值将创建一个在所有生成的块之间共享的运行时文件模板),尤其是运行时和清单。如果不单独提取这些代码,即使代码没有变化,打包后的文件名仍然会发生变化,导致无法命中缓存。HashedModuleIdsPlugin在webpack4中用于生成hash值作为模块id,在webpack5中不再需要。moduleIds:'deterministic'用于确保模块id不会随着解析顺序的变化而变化。生产环境默认开启。缓存的方式(从构造层面)webpackV4cache-loader:建议在开销较大的loader前加,如babel-loader、vue-loader等;dll:依赖不常变化的版本(react、lodash),单独生成动态链接库(bundle),提高构建速度。它需要与DllPlugin和DllReferencePlugin一起使用。通过引用dll的manifest文件将依赖名称映射到模块id,然后在需要的时候使用内置的__webpack_require__函数来require它们,推荐在开发模式下使用webpackV5文件系统缓存。配置方法见上面关键代码。作用是让Webpack运行时存在于内存中的那些缓存,而不是loader的产物,更不用说dll了,根据Webpack运行环境的不同,dev开发时仍然使用MemoryCachePlugin,而IdleFileCachePlugin在开发时使用建造。dev/build的二次编译速度会比cache-loader快很多。一些原则说起期待已久的Webpack5持久化缓存优化了整个构建过程。Relationships,只编译依赖树上相关的文件,大大提高了构建速度。经官方测试,由16000个模块组成的单页应用速度可提升98%!值得注意的是,持久缓存会将缓存存储到磁盘。对于连续构建过程,第一次构建是一个完整的构建,然后它会将相关产品序列化并缓存在磁盘中(序列化)。后续构建的具体过程可以根据上次的缓存进行:读取磁盘缓存->校验模块->解压模块内容。因为模块之间的关系不会被显式缓存,所以在每次构建过程中仍然需要验证模块之间的关系。这个验证过程和正常的webpack分析依赖的逻辑是完全一致的。的。解析器缓存也可以持久缓存。一旦解析器缓存被验证并发现完全匹配,它就可以用于快速查找依赖项。如果解析器缓存验证失败,则直接执行解析器的正常构建逻辑。缓存安全设计UnsafeCache是??webpack4.x构建时基于时间戳比较策略的缓存方式。它有两个维度,unsafeCache用于resolve(解析器)和unsafeCache用于模块(module)。如果同时启用,webpack从入口文件开始,通过resolve规则解析所有依赖文件,保存模块间的依赖关系和解析后的文件内容,并存储依赖关系的最后一次更改时间(时间戳)。一旦相同的Reference,返回缓存。webpack5.x版本已经放弃了这种缓存策略。unsafeCache默认只开启cache选项,是node_modules下的依赖,通过判断是否有文件系统序列化的文件信息来判断是否需要重建。safeCache模块之间的依赖关系由基于内容比较的算法(contentHash)记录并存储在ModulGraph类中的weakmap中,这比依赖时间戳更可靠。缓存容量限制除了需要考虑缓存的安全性,缓存的容量限制也不容忽视。缓存不能无限叠加。这就涉及到经典的LRU缓存算法(LeastRecentlyUsed)。单向链表增删节点O(1),查找O(n)双向链表加哈希表组合O(1)LRU分析当有热点数据时,LRU的效率很好,但是零星和周期性的批量操作会导致LRU命中率急剧下降,缓存污染严重。LRU算法redis的改进方案采用改进算法LIRS、LRU-K等,感兴趣的同学可以参考作者:丁楠。我不生产代码,我是代码的搬运工。
