按照现在构建方式,它使用的文件名是有问题的。它无法有效利用客户端级别的缓存,因为无法判断文件是否已更改。可以通过在文件名中包含哈希来实现缓存失效。
占位符
Webpack 为此提供占位符。这些字符串用于将特定信息附加到 Webpack 输出。最有价值的是:
[id]
- 返回块 ID。[path]
- 返回文件路径。[name]
- 返回文件名。[ext]
- 返回扩展名。[ext]
适用于大多数可用字段。MiniCssExtractPlugin
是一个值得注意的例外。[hash]
- 返回构建哈希。如果构建的任何部分发生变化,这也会发生变化。[chunkhash]
- 返回特定的入口块哈希。配置中定义的每个entry
都接收自己的哈希。如果入口的任何部分发生更改,则哈希值也会更改。[chunkhash]
比[hash]
更细化。[contenthash]
- 返回基于内容生成的哈希。
hash
和 chunkhash
仅用于生产目的,因为哈希值在开发期间没有太大的用处。
我们可以使用特定的语法,对
hash
和chunkhash
进行切片:[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 以解决问题。