如何在Webpack中使用缓存破坏技术?

45

在使用Webpack之前,我总是依赖以下模式进行"缓存破坏":

<script src="foo.js?cacheBust=12345" />

这里的12345 是服务器在每次构建时为我生成的一个令牌(它可以是Git哈希,但在我的情况下不是)。

使用Webpack后,现在有两个文件:build.jschunk.1.js。由于我使用普通的脚本标签引入第一个文件,我可以使用上述模式:

<script src="build.js?cacheBust=12345" />

然而,此时 build.js 去获取 chunk.1.js 时,并没有包含缓存破坏后缀。

我希望Webpack自动添加 ?cacheBust=12345,但我不知道构建时的 12345 部分,因此无法在我的 webpack.config 中包含它。相反,我必须等待HTML页面被评估,在此时从服务器获取令牌。

所以,我的问题是,是否有任何方法让Webpack查看用于获取初始文件的参数(例如:?cacheBust=12345),并在获取其他文件时附加同样的参数?


将查询参数添加到同一文件中,当其内容发生更改时,您所期望的技术不会导致缓存失效。请参阅:https://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/ - Butifarra
1
我觉得你可能读那篇文章太快了:它并没有说这种技术行不通。文章实际上是说:如果像Steve Souders一样使用Squid代理,这种技术就不起作用。服务器如何处理请求完全取决于服务器本身,但根据我的经验,大多数主流的服务器端框架,以及大多数Web服务器(如Apache)确实会区别对待foo.pngfoo.png?v=1 - machineghost
本文的重点在于强调一个事实,即可能存在在客户端和服务器之间的任何代理服务器都很可能会丢弃查询参数并检查文件的缓存版本。当代理服务器上是这种情况时,您的新文件版本将不会被检索,用户将看到旧的缓存版本。 - Butifarra
如果您查看了第1部分@Everettes的答案,它确实回答了您的问题。使用[chunkhash]的chunkFilename是打破缓存的最佳方法。如果您将这些文件存储在git中,您可以git rm这些文件,进行构建,然后git add。哈希值未更改的文件将被简单地“恢复”,而哈希值已更改的文件将消失。考虑解决方案,而不是机制。 - boatcoder
这是一个老生常谈的争论,但Souders臭名昭著的博客文章把事情说错了。使用查询字符串进行高速缓存破坏比他想象的要有效得多。 - Ringo
6个回答

58

如果您希望以“webpack方式”实现缓存失效:

1. 输出文件哈希命名

将输出文件名更改为生成的哈希名称(在构建阶段)

output: {
    path: '/',
    filename: '[hash].js',
    chunkFilename: '[chunkhash].js',
},

从那一点开始,你的foo.jschunk.1.js将被称为e883ce503b831d4dde09.jsf900ab84da3ad9bd39cc.js。值得注意的是,生成这些文件通常与制作生产和更新cacheBust值有关。

2. 如何包含名称未知的文件?

从现在起,你的foo.js - 主文件的名称是未知的。要提取文件名,可以使用AssetsPlugin

const AssetsPlugin = require('assets-webpack-plugin');
const assetsPluginInstance = new AssetsPlugin();

并将此插件添加到webpack.config.js

plugins: [
    assetsPluginInstance
]

webpack-assets.json 文件中,你应该能看到类似以下内容的东西:

{
    "main": {
        "js": "/e883ce503b831d4dde09.js"
    }
}

您可以使用此文件来指向主要的.js文件。有关更多详细信息,请阅读此答案

3. 受益时间

我猜,如果您由于chunk.2.js文件的修改而制作应用程序,则会更改文件路径从

- build.js?cacheBust=12345
- chunk.1.js?cacheBust=12345
- chunk.2.js?cacheBust=12345
- chunk.2.js?cacheBust=12345

转向新的对象

- build.js?cacheBust=12346   // modified referation to chunk.2.js file
- chunk.1.js?cacheBust=12346
- chunk.2.js?cacheBust=12346 // modified
- chunk.2.js?cacheBust=12346
如果您使用上述解决方案,您将获得免费缓存确定。现在文件将被称为

(之前的生产)

- e883ce503b831d4dde09.js
- f900ab84da3ad9bd39cc.js
- 5015cc82c7831915903f.js
- 8b6de52a46dd942a63a7.js

(新的生产)

- c56322911935a8c9af13.js // modified referation to chunk.2.js file
- f900ab84da3ad9bd39cc.js
- cd2229826373edd7f3bc.js // modified
- 8b6de52a46dd942a63a7.js

现在只有主文件chunk.2.js的名称被更改了,使用webpack方式可以免费获得这些更改。

您可以在这里阅读更多关于长期缓存的信息。


谢谢。虽然我很感激“Webpack方式”的解释,但你并没有回答我的问题。Webpack是否无法将?cacheBust=12345附加到它导入的文件中? - machineghost
可以使用不同的语法来追加,而不是chunk.1.js?cacheBust=12345,如chunk.1.12345.js - Everettss
很遗憾,我正在尝试让文件每次从服务器的角度看来具有完全相同的URL,因为服务器端只有一个文件。 "缓存破坏令牌"(cacheBust=12345)需要出现在?之后,以便客户端将其视为URL的一部分(从而破坏缓存),但服务器会忽略它(并且每次只看到实际的文件路径)。 - machineghost
@Vikas 使用 file-loader https://github.com/webpack-contrib/file-loader 作为默认选项,它会将原始文件名转换为哈希值。 - Everettss
这个机制是否能够确保,比如说我有一个标签 =“我的文本”,然后我将其更新为“我的更新文本”,然后构建解决方案。只需要按Ctrl + R,我应该能够看到“我的更新文本”作为标签吗? - tRuEsAtM
显示剩余2条评论

24
您可以简单地这样做。
output: {
    filename: '[name].js?t=' + new Date().getTime(),
    chunkFilename: '[name]-chunk.js?t=' + new Date().getTime(),
    publicPath: './',
    path: path.resolve(__dirname, 'deploymentPackage')
}

1
你如何在HTML的script src=中使用这个文件名?谢谢! - xims
1
@xims,你可以直接引用HTML,块将被命名为0-chunk.js,你只需要引用它, 我想知道为什么你要给出块引用。 - VizardCrawler
3
抱歉我没有表达清楚。 目前,html 代码是 <script src="app.js"></script>。 如果我将 webpack 的 output.filename 改为 app.js?t=123,我该如何在 html 的 script 标签中使用这个动态文件名? - xims
根据@Everettss的回答,您可能还可以使用模式[name]-chunk.js?t=[hash] - 这仍然为您提供可读的文件名,并且应该只有在文件更改时才更改哈希。 - Andreas
这是我见过的奇怪黑科技之一,而且我是从Webpack 5版本开始说的。 - Hyfy
显示剩余3条评论

16

您可以使用 HtmlWebpackPlugin

webpack.js.org/plugins/html-webpack-plugin的描述如下:

... 插件简化了创建 HTML 文件以服务于您的 webpack bundles。这对于包含在文件名中每次编译都会更改哈希的 webpack bundles 特别有用...

webpack.config.js 的一部分:

// ...
const HtmlWebpackPlugin = require('html-webpack-plugin');
// ...
module.exports = {
   // ...
   plugins: [
      new HtmlWebpackPlugin({
         template: './assets/index.html',
         hash: true,
      }),
      // ...
   ]
};
如果 hash: true,则将唯一的 webpack 编译哈希附加到所有包含的脚本和 CSS 文件中。这对于缓存破坏非常有用。 更多关于 HtmlWebpackPlugin 选项 的信息请参见github.com/jantimon/html-webpack-plugin。 感谢这个选项,我得到了输出 HTML 文件。
<!DOCTYPE html>
<html>
   <head>
      <!-- ... rest of my head code ... -->
      <link href="./css/styles.css?f42fdf96e2f7f678f9da" rel="stylesheet">
   </head>
   <body>
      <!-- ... rest of my body code ... -->
      <script type="text/javascript" src="./js/index.bundle.js?f42fdf96e2f7f678f9da"></script>
   </body>
</html>

我的项目源代码: github.com/cichy380/html-starter-bs4-webpack


1
这在v5.3.1版本中无法工作。相反,我通过运行“npm view html-webpack-plugin@* version”查找了最新的v2版本,发现2.30.1是该主要版本的最新版本,然后运行“npm i html-webpack-plugin@2.30.1 --save-dev --save-exact”来锁定兼容的软件包。现在完美地工作了,谢谢! - TaeKwonJoe
对于 Laravel,首先安装 webpack-blade-native-loader,它将 Blade 编译为 HTML 并支持在生成的 HTML 文件上运行 HtmlWebpackPlugin(但仅当您的 HTML 不是动态的且不像每个用户一样变化时才有用,通常不是这种情况 :/)。 - Top-Master

4
以下是Webpack v5的内容。
webpack.config.js
module.exports = {
    // ...
    output: {
        filename: "[name].bundle.[chunkhash].js",
        path: path.resolve(__dirname, "dist"),
        assetModuleFilename: "images/[hash][ext][query]"
    },
    // ...
    optimization: {
        moduleIds: "deterministic",
    }
    // ...
}

使用哈希值是很重要的,比如 [chunkhash]

注意,有很多不同的方法可以实现。

来源:https://webpack.js.org/guides/caching/


3
如果你想在Webpack中以格式name.js?cacheBust=1234获取哈希值,可以这样做:
output: {
  filename: '[name].js',
  chunkFilename: '[name].js?cacheBust=[chunkhash]',
},

我正在使用Laraval Mix在Webpack中进行类似的操作,我在这个Github问题中找到了答案:https://github.com/JeffreyWay/laravel-mix/issues/2131

1
有许多方法可以进行缓存破坏,webpack 也是其中之一。我经常使用后缀技术,并在此帖子中寻找答案。我与 OP 使用不同的块,因为我只在通过 webpack 运行的 javascript 中引用该块:
const componentPromise = import(/* webpackChunkName: "myDelayedSource" */ "path/to/myComponent/myComponent.js");

我找到的缓存清除解决方案(在js中)就是简单地更改块的名称:
const componentPromise = import(/* webpackChunkName: "myDelayedSource_2020-05-14" */ "path/to/myComponent/myComponent.js");

这不会给文件添加后缀,但它会更改文件名,引用该文件的脚本也会使用新的文件名。
希望这能帮到某些人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接