在前端开发中,我们经常使用 webpack 来打包我们的代码。webpack 提供了多种 hash 算法来生成唯一的文件名,包括文件 hash 和 chunkhash。虽然这两种 hash 算法的使用方式很相似,但它们的区别却非常关键。本文将深入探讨 webpack 文件 hash 和 chunkhash 的区别,并且提供代码示例。
文件 hash 和 chunkhash 的定义
在讨论文件 hash 和 chunkhash 的区别之前,我们先来了解一下这两个概念。
- 文件 hash:在 webpack 打包时,每个文件都会生成一个唯一的 hash 值,这个 hash 值可以作为该文件的文件名。
- chunkhash:在 webpack 打包时,各个模块(module)的代码被分配到不同的 chunk(代码块)中。每个 chunk 都会生成一个唯一的 hash 值,可以作为该 chunk 的文件名。
文件 hash 和 chunkhash 的区别
虽然文件 hash 和 chunkhash 都用于生成唯一的文件名,但它们的区别是非常关键的。下面我们将从以下三个方面来探讨它们的区别。
1. 文件缓存
文件 hash 会受到所有文件的影响。也就是说,当一个文件的内容被修改后,它的 hash 值就会改变,导致所有使用该文件的缓存都失效。
例如,我们有两个文件 a.js 和 b.js,它们的文件名根据文件 hash 生成,分别是 a.35d7c9f.js 和 b.ff7e3a3.js。当我们将这两个文件引入到 index.html 文件中时,index.html 文件会变成这样:
-- -------------------- ---- ------- --------- ----- ------ ------ ----- ---------------- --------------- ------- ------ ------- ---------------------------- ------- ---------------------------- ------- -------展开代码
当我们修改 a.js 文件的内容后,重新打包时,两个文件的文件名会变化,例如 a.210e18d.js 和 b.ff7e3a3.js。此时,index.html 中引用的文件名也会变化,变成这样:
-- -------------------- ---- ------- --------- ----- ------ ------ ----- ---------------- --------------- ------- ------ ------- ---------------------------- ------- ---------------------------- ------- -------展开代码
这就导致了浏览器重新下载了 a.js 和 b.js,即使 b.js 的内容并没有变化。这样会导致我们的页面加载速度变慢,给用户带来不好的体验。
而 chunkhash 只会受到该 chunk 中各个模块的影响。也就是说,当某个模块的内容被修改后,只有该模块所在的 chunk 的 hash 值会改变,其他 chunk 的 hash 值不会改变。这就避免了文件缓存的问题。
例如,我们有两个文件 a.js 和 b.js,它们都被打包到了一个名为 main 的 chunk 中,chunk 名称根据 chunkhash 生成,例如 main.739979f.js。当我们将该文件引入到 index.html 文件中时,index.html 文件会变成这样:
-- -------------------- ---- ------- --------- ----- ------ ------ ----- ---------------- --------------- ------- ------ ------- ------------------------------- ------- -------展开代码
当我们修改 a.js 文件的内容后,重新打包时,只有 main.chunkhash.js 文件名会变化,因为它是该 chunk 中模块变化的结果。index.html 中引用的文件名还是 main.739979f.js,不会发生变化。这样,浏览器只需要重新下载 main.chunkhash.js 文件,而不需要重新下载 b.js 文件。
2. 共享代码
chunkhash 还具有共享代码的功能。在 webpack 4 中,如果我们将多个 entry point 的公共模块提取到一个名为 commons 的 chunk 中,那么该 chunk 的 hash 值将会基于 common chunk 中的模块和当前 chunk 中的模块共同生成。
例如,我们有两个 entry point,分别为 app1 和 app2。它们都引入了模块 a.js 和 b.js。我们可以使用 SplitChunksPlugin 将 a.js 提取到一个名为 commons 的 chunk 中,以避免出现重复的代码。
-- -------------------- ---- ------- -- ----------------- -------------- - - ------ - ----- ---------------- ----- --------------- -- ------- - ----- ----------------------- -------- --------- --------------------- -- ------------- - ------------- --------- ------------ - ------- ------ ------------ - -------- - ----- ------------------------- ----- ---------- ------- ----- - - - - --展开代码
此时生成的文件名就会基于 common chunk 中的模块和当前 chunk 中的模块共同生成 hash 值。例如,当我们打包 app1 时,其文件名为 app1.2b7ab26.js,其中 2b7ab26 是 common chunk 和 app1 中代码的 hash 值。
-- -------------------- ---- ------- --------- ----- ------ ------ ----- ---------------- --------------- ------- ------ ------- -------------------------------------------- ------- -------------------------------------------- ------- ------------------------------- ------- -------展开代码
同样的,打包 app2 时,其文件名为 app2.5fd4648.js,其中 5fd4648 是 common chunk 和 app2 中代码的 hash 值。
-- -------------------- ---- ------- --------- ----- ------ ------ ----- ---------------- --------------- ------- ------ ------- -------------------------------------------- ------- -------------------------------------------- ------- ------------------------------- ------- -------展开代码
这样,我们就可以实现将公共代码提取到一个 chunk 中,并且该 chunk 的 hash 值不受 entry point 影响的目的。
3. 持久化
持久化是指在修改文件后,文件的 hash 值保持不变的情况。这在某些场景下很有用,比如发布静态资源到 CDN 上。在这种情况下,我们需要保证文件的 hash 值不变,以免浏览器重新下载资源。
文件 hash 不支持持久化,当我们修改文件时,其 hash 值也会发生变化。而 chunkhash 支持持久化,当我们修改一个模块时,只有该模块所在的 chunk 的 hash 值会改变,其他 chunk 的 hash 值不会改变。这样就保证了文件的 hash 值在修改文件后不会发生变化。
示例代码
下面是使用 webpack 实现文件 hash 和 chunkhash 的示例代码。
-- -------------------- ---- ------- -- ----------------- ----- ---- - ---------------- -------------- - - ------ ----------------- ------- - --------- ------------------- -- -- ---- -- --------- ------------------------ -- --------- ----- ----------------------- ------- - -- -- -------- ------ - --- - ---- ----------- ------ - --- - ---- ----------- ------ ------ -- ------ ------ -------- ----- - ------------------- - -- ------ ------ -------- ----- - ------------------- -展开代码
我们可以先使用文件 hash 打包代码,打包后的文件名为 bundle.87279c14abef4d4e001b.js。随后修改 index.js 的代码,再次打包,发现打包后的文件名变为 bundle.99c184bb2e66fb1c23cd.js。这就是文件 hash 存在的问题所在。
将代码切换到 chunkhash,我们再次修改 index.js 的代码,再次打包,发现打包后的文件名仅仅是 main.chunkhash.js 发生了变化,对应的文件名变为 main.5142ac06e782641cd653.chunkhash.js。在此基础上,我们修改 foo.js 的代码再次打包,仅仅是 main.chunkhash.js 的文件名发生了变化,变成了 main.e8cb0a623ebf38470b0c.chunkhash.js。这样就能减少不必要的文件下载和提高页面的性能。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67c96114e46428fe9e0bd11b