将哈希值添加到文件名中

按照现在构建方式,它使用的文件名是有问题的。它无法有效利用客户端级别的缓存,因为无法判断文件是否已更改。可以通过在文件名中包含哈希来实现缓存失效。

占位符

Webpack 为此提供占位符。这些字符串用于将特定信息附加到 Webpack 输出。最有价值的是:

  • [id] - 返回块 ID。
  • [path] - 返回文件路径。
  • [name] - 返回文件名。
  • [ext] - 返回扩展名。[ext] 适用于大多数可用字段。MiniCssExtractPlugin 是一个值得注意的例外。
  • [hash] - 返回构建哈希。如果构建的任何部分发生变化,这也会发生变化。
  • [chunkhash] - 返回特定的入口块哈希。配置中定义的每个 entry 都接收自己的哈希。如果入口的任何部分发生更改,则哈希值也会更改。[chunkhash][hash] 更细化。
  • [contenthash] - 返回基于内容生成的哈希。

hashchunkhash 仅用于生产目的,因为哈希值在开发期间没有太大的用处。

我们可以使用特定的语法,对 hashchunkhash 进行切片:[chunkhash:4],像 8c4cbfdb91ff93f3f3c5 这样的哈希会最后会变为 8c4c

还有更多可用选项,您甚至可以修改散列和摘要类型,就像 loader-utils 文档中讨论的那样。

占位符示例

假设您具有以下配置:

{
  output: {
    path: PATHS.build,
    filename: "[name].[chunkhash].js",
  },
},

Webpack 会基于它生成这些文件名:

main.d587bbd6e38337f5accd.js
vendor.dc746a5db4ed650296e1.js

如果与块相关的文件内容变化了,则哈希值也会改变,因此缓存变得无效。更准确地说,浏览器发送新文件的新请求。如果只是 main 更新了,则只需要再次请求该文件。

通过生成静态文件名并通过查询字符串(即main.js?d587bbd6e38337f5accd)使缓存无效,可以实现相同的结果。问号背后的部分使缓存无效。根据 Steve Souders 的说法,将哈希值附加到文件名是最高性能的方法。

设置哈希

我们需要调整构建以生成适当的哈希。图像和字体应该使用 hash,而块应该使用 chunkhash

webpack.config.js

const productionConfig = merge([

  {
    output: {
      chunkFilename: "[name].[chunkhash:4].js",
      filename: "[name].[chunkhash:4].js",
    },
  },

  ...
  parts.loadImages({
    options: {
      limit: 15000,
      name: "[name].[hash:4].[ext]",
    },
  }),
  ...
]);

file-loader 对于 [hash] 的定义与 Webpack 中的有所不同,它是根据文件内容计算的。有关详细信息,请参阅 file-loader 文档

如果您在 CSS 提取时也使用了 chunkhash,这会导致问题,因为所有导入 CSS 的 JavaScript 代码都会指向同一入口,这意味着如果应用程序代码或 CSS 发生了变化,两者都会失效。

因此,您可以使用 contenthash 来代替 chunkhash,前者是基于提取的内容生成的:

webpack.parts.js

exports.extractCSS = ({ include, exclude, use }) => {
  // Output extracted CSS to a file
  const plugin = new MiniCssExtractPlugin({
    filename: "[name].[contenthash:4].css",
  });

  ...
};

对哈希值进行切片使得输出更适合本书。实际上,您可以跳过切片。

如果你生成一个build now(npm run build),你会看到一些东西:

Hash: fb67c5fd35454da1d6ff
Version: webpack 4.1.1
Time: 3034ms
Built at: 3/16/2018 6:18:07 PM
                   Asset       Size  Chunks             Chunk Names
               0.0847.js  161 bytes       0  [emitted]
    vendors~main.d2f1.js   96.8 KiB       1  [emitted]  vendors~main
            main.745c.js   2.25 KiB       2  [emitted]  main
           main.5524.css    1.2 KiB       2  [emitted]  main
   vendors~main.3dd5.css   1.32 KiB       1  [emitted]  vendors~main
           0.0847.js.map  203 bytes       0  [emitted]
vendors~main.d2f1.js.map    235 KiB       1  [emitted]  vendors~main
        main.745c.js.map   11.4 KiB       2  [emitted]  main
              index.html  349 bytes          [emitted]
Entrypoint main = vendors~main.d2f1.js ...
...

文件现在有整齐的哈希了。为了证明它适用于样式,你可以尝试改变 src/main.css,看看重建时哈希是否会改变。

但是还有一个问题。如果更改应用程序代码,它也会使外部依赖文件失效!解决此问题需要提取一个 manifest 文件,但在此之前,您可以使用模块ID来改进生产构建的处理方式。

总结

将与文件内容相关的哈希包括在其名称中允许在客户端使它们无效。如果哈希值已更改,则会强制客户端再次下载该资源。

回顾一下:

  • Webpack 的占位符允许您对文件名进行调整,并允许您为它们包含哈希。
  • 最有价值的占位符是 [name][chunkhash][ext]。chunkhash 是基于入口的。
  • 如果您正在使用 MiniCssExtractPlugin,您应该使用[contenthash]。这样,只有在内容发生更改时,生成的资源才会失效。

尽管我们在项目中用上了哈希,但是得到的结果也不完美。问题是如果应用程序发生更改,它也会使供应商包无效。下一章将深入探讨该主题,并向您展示如何提取 manifest 以解决问题。

用户头像
登录后发表评论