webpack 文件 hash 与 chunkhash 的区别

阅读时长 8 分钟读完

在前端开发中,我们经常使用 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

纠错
反馈

纠错反馈