Skip to content

hashing dilemma

rollup V3 has a new hashing algorithm, which is more efficient and faster.

problem

previous hashing algorithm

算法执行流程如下:

  1. 渲染所有模块,除了动态导入和 import.meta 块引用。
  2. 基于此,计算块中所有模块的内容哈希。
  3. 通过考虑所有已知的依赖项和可能在块包装器中添加的动态内容来扩展这些哈希。
  4. 更新动态导入和 import.meta 块引用。
  5. 渲染包含所有静态导入和导出的块包装器。
  6. 通过 renderChunk 插件钩子运行结果。

存在的问题:

  1. 在 renderChunk 中的任何转换都会被完全忽略。也就是说 renderChunk 插件中的任何转换都不会影响 chunkhash 值,这就打破了 content hash 的初衷。
  2. 考虑到块包装器中的每一个可能变化都是一项脆弱的工作。
  3. 哈希值在技术上是不稳定的,因为它们可能考虑了实际上与结果无关的事物。

可能的解决方案:排序渲染:

一种解决方法是首先渲染所有没有任何依赖项的块,然后迭代地渲染其他块,这些块只包含已经渲染的块作为依赖项。直到所有块都被渲染。在某些情况下这样做可以起作用,但这种方法有几个缺点:

  1. 它不支持块之间的循环依赖关系。这是一个非常重要的特性,因为在这种情况下,循环依赖也意味着两个块相互动态导入彼此。此外,Rollup 在动态导入时严重依赖于此机制,通过将导入块和目标导入之间共享的所有依赖项移至导入块中,在被引入的块中出现了静态引用来自于导入模块。

    举一个例子:

    假设有两个模块,模块A和模块B,其中:

    • 模块A动态导入模块B。
    • 模块B动态导入模块A。

    在这种情况下,rollup 会将模块A和模块B之间共享的所有依赖项移至模块A中。这意味着在模块B中会出现对模块A的静态导入。这种处理方式确保了在动态导入时,所有共享的依赖项都已被正确加载。这种机制对于处理复杂的模块依赖关系非常重要,尤其是在大型项目中,模块之间的依赖关系可能非常复杂且相互交织。通过这种方式,rollup 能够有效地管理和优化模块的加载顺序和依赖关系。

  2. 这意味着我们需要在渲染块之前了解块的所有依赖关系,即使 renderChunk 钩子可能会引入额外的依赖关系。

new hashing algorithm(Hash placeholders)

因此,我决定采用一种不同的解决方案,其工作原理如下:

  1. 为每个块分配一个初步的块名称。如果文件名中没有哈希,这将是最终的文件名,但如果文件名中包含哈希,则会包含一个等长的占位符。
  2. 渲染块中的所有模块。由于我们已经有了初步的文件名,因此可以直接渲染所有动态导入和 import.meta 块引用。
  3. 渲染块包装器。我们也使用初步的文件名进行块导入。
  4. 通过 renderChunk 钩子运行块。
  5. 通过将块中的所有占位符替换为默认占位符,然后生成哈希,计算仅内容的哈希。
  6. 一旦所有块都完成此操作,通过搜索每个块中包含哪些占位符并更新块哈希,计算最终哈希,同时考虑所有传递依赖项的仅内容哈希。
  7. 用最终哈希替换占位符。由于我们确保占位符与哈希的长度相同,因此不需要更新源映射。

为了避免意外替换非占位符,占位符利用了 javascript 支持 Unicode 字符的事实。我使用了保留平面中的四个随机字符,目前是 \uf7f9\ue4d3 作为占位符的开始和 \ue3cc\uf1fe 作为占位符的结束。

这种方法还允许我们在 renderChunk 中完全访问块图,尽管此时名称是初步的。但由于 rollup 不对 renderChunk 的输出做假设,您现在可以在该钩子中自由注入块名称。

总的来说,这改变了插件钩子执行流程图:

parallel
sequential
first
async
sync

以下是先前的钩子执行流程图:

对比一下可以很直观的看出变化点

  1. 执行时机上的变化

    • bannerfooterintrooutro 钩子的执行时机延后,原先是在 renderStart 钩子执行完后执行。现在是在 renderChunk 钩子执行前执行。
    • augmentChunkHash 钩子的执行时机延后,原先是在 renderDynamicImport 钩子决策之后执行。现在是在 renderChunk 钩子执行后执行。
  2. 执行方式上的变化

  • bannerfooterintrooutro 钩子由原先的并行执行改为现在的串行执行。

Released under the MIT License. (bfe9f65)