防止 RequireJS 缓存所需的脚本

IT技术 javascript jquery requirejs
2021-01-14 15:37:39

RequireJS 似乎在内部做一些事情来缓存所需的 javascript 文件。如果对所需文件之一进行更改,则必须重命名该文件才能应用更改。

将版本号作为查询字符串参数附加到文件名末尾的常见技巧不适用于 requirejs <script src="jsfile.js?v2"></script>

我正在寻找一种方法来防止 RequireJS 所需脚本的这种内部缓存,而不必在每次更新时重命名我的脚本文件。

跨平台解决方案:

我现在urlArgs: "bust=" + (new Date()).getTime()在开发和urlArgs: "bust=v2"生产期间使用自动缓存破坏,在推出更新的所需脚本后,我增加硬编码版本号。

笔记:

@Dustin Getz 在最近的回答中提到,当 Javascript 文件像这样持续刷新时,Chrome 开发人员工具将在调试期间删除断点。一种解决方法是编写debugger;代码以在大多数 Javascript 调试器中触发断点。

特定于服务器的解决方案:

有关可能更适合您的服务器环境(例如 Node 或 Apache)的特定解决方案,请参阅下面的一些答案。

6个回答

RequireJS 可以配置为向每个脚本 url 附加一个值以进行缓存破坏。

从 RequireJS 文档(http://requirejs.org/docs/api.html#config):

urlArgs:附加到 RequireJS 用于获取资源的 URL 的额外查询字符串参数。当浏览器或服务器配置不正确时,对缓存 bust 最有用。

例如,将“v2”附加到所有脚本:

require.config({
    urlArgs: "bust=v2"
});

出于开发目的,您可以通过附加时间戳来强制 RequireJS 绕过缓存:

require.config({
    urlArgs: "bust=" + (new Date()).getTime()
});
...作为性能优化器,您可以使用 Math.random() 而不是 (new Date()).getTime()。它更美观,而不是创建对象,速度更快jsperf.com/speedcomparison
2021-03-20 15:37:39
我认为每次都破坏缓存是一个糟糕的主意。不幸的是 RequireJS 没有提供另一种选择。我们确实使用了 urlArgs,但不要为此使用随机或时间戳。相反,我们使用我们当前的 Git SHA,这样只会在我们部署新代码时发生变化。
2021-03-24 15:37:39
很有帮助,谢谢。urlArgs: "bust=" + (new Date()).getTime()在开发和urlArgs: "bust=v2"生产期间使用自动缓存破坏,在推出更新的所需脚本后,我增加硬编码版本号。
2021-04-09 15:37:39
如果提供“v2”字符串本身的文件被缓存,这个“v2”变体如何在生产中工作?如果我将一个新应用程序发布到生产环境中,添加“v3”不会做任何事情,因为应用程序很乐意继续使用缓存的 v2 文件,包括带有 v2 的旧配置urlArgs
2021-04-09 15:37:39
你可以把它弄小一点: urlArgs: "bust=" + (+new Date)
2021-04-11 15:37:39

不要为此使用 urlArgs!

要求脚本加载尊重 http 缓存标头。(脚本加载了动态插入的<script>,这意味着请求看起来就像加载任何旧资产一样。)

使用正确的 HTTP 标头为您的 javascript 资产提供服务,以在开发期间禁用缓存。

使用 require 的 urlArgs 意味着您设置的任何断点都不会在刷新时保留;您最终需要debugger在代码中随处放置语句。坏的。我在使用urlArgsgit sha 进行生产升级期间用于缓存破坏资产;然后我可以将我的资产设置为永久缓存,并保证永远不会有过时的资产。

在开发中,我使用复杂的mockjax配置模拟所有 ajax 请求,然后我可以在 javascript-only 模式下为我的应用程序提供服务,并使用10 行 python http 服务器关闭所有缓存这对我来说已经扩展到一个相当大的“企业”应用程序,其中包含数百个安静的 web 服务端点。我们甚至有一个签约设计师,他可以使用我们真正的生产代码库,而无需让他访问我们的后端代码。

+1 到 !!!不要在生产中使用 urlArgs !!!. 想象一下您的网站有 1000 个 JS 文件(是的,可能!)并且它们的负载由 requiredJS 控制的情况。现在您发布了 v2 或您的站点,其中仅更改了少量 JS 文件!但是通过添加 urlArgs=v2,您会强制重新加载所有 1000 个 JS 文件!你会付出很多流量!只应重新加载修改过的文件,所有其他文件都应以状态 304(未修改)响应。
2021-03-14 15:37:39
对于在节点上使用 http-server 的任何人(npm install http-server)。您还可以使用 -c-1(即 http-server -c-1)禁用缓存。
2021-03-15 15:37:39
@JamesP.Wright,因为(至少在 Chrome 中)当您为页面加载时发生的事情设置断点,然后单击刷新时,不会命中断点,因为 URL 已更改并且 Chrome 已删除断点。我很想知道一个仅限客户端的解决方法。
2021-03-17 15:37:39
您可以在 Chrome 中禁用缓存以解决开发过程中的调试问题:stackoverflow.com/questions/5690269/...
2021-03-23 15:37:39
谢谢,达斯汀。如果您找到解决此问题的方法,请发布。同时,您可以debugger;在代码中使用任何您希望断点持续存在的地方。
2021-03-29 15:37:39

urlArgs 解决方案有问题。不幸的是,您无法控制您和用户的 Web 浏览器之间可能存在的所有代理服务器。不幸的是,其中一些代理服务器配置为在缓存文件时忽略 URL 参数。如果发生这种情况,您的 JS 文件的错误版本将交付给您的用户。

我终于放弃了,直接在 require.js 中实现了我自己的修复如果您愿意修改 requirejs 库的版本,此解决方案可能适合您。

你可以在这里看到补丁:

https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67

添加后,您可以在 require 配置中执行以下操作:

var require = {
    baseUrl: "/scripts/",
    cacheSuffix: ".buildNumber"
}

使用您的构建系统或服务器环境替换buildNumber为修订版 ID/软件版本/最喜欢的颜色。

像这样使用 require:

require(["myModule"], function() {
    // no-op;
});

会导致 require 请求这个文件:

http://yourserver.com/scripts/myModule.buildNumber.js

在我们的服务器环境中,我们使用 url 重写规则来剥离 buildNumber,并提供正确的 JS 文件。这样我们实际上不必担心重命名我们所有的 JS 文件。

该补丁将忽略任何指定协议的脚本,并且不会影响任何非 JS 文件。

这对我的环境很有效,但我意识到有些用户更喜欢前缀而不是后缀,修改我的提交以满足您的需求应该很容易。

更新:

在 pull request 讨论中,requirejs 作者建议这可能作为一个解决方案来为修订号添加前缀:

var require = {
    baseUrl: "/scripts/buildNumber."
};

我没有试过这个,但暗示这会请求以下 URL:

http://yourserver.com/scripts/buildNumber.myModule.js

对于许多可以使用前缀的人来说,这可能非常有效。

以下是一些可能的重复问题:

RequireJS 和代理缓存

require.js - 如何将所需module的版本设置为 URL 的一部分?

现代前端系统只是重写 JS 文件并在文件名中使用 MD5 总和,然后重写 HTML 文件以在构建时使用新文件名,但是对于前端代码由服务器端提供服务的遗留系统,这会变得棘手。
2021-03-13 15:37:39
我真的很想看到你的更新进入官方的 requirejs 构建。requirejs 的主要作者也可能感兴趣(请参阅上面@Louis 的回答)。
2021-03-15 15:37:39
@BumbleB2na - 随时对 PullRequest ( github.com/jrburke/requirejs/pull/1017 )发表评论,jrburke 似乎不感兴趣。他确实提出了一个使用文件名前缀的解决方案,我会更新我的答案以包含它。
2021-03-18 15:37:39
不错的更新。我想我确实喜欢作者的这个建议,但这只是因为我最近一直在使用这个命名约定:/scripts/myLib/v1.1/. 我尝试在我的文件名中添加后缀(或前缀),可能是因为 jquery 就是这样做的,但过了一段时间后,我 [变得懒惰并] 开始增加父文件夹上的版本号。我认为这让我在大型网站上的维护变得更容易,但是,现在您让我担心 URL 重写的噩梦。
2021-04-02 15:37:39
当我需要在 jspx 文件中使用一些 js 时,这是否有效?,像这样 <script data-main="${pageContext.request.contextPath}/resources/scripts/main" src="${pageContext.request.contextPath}/resources/scripts/require.js"> <jsp:text/> </script> <script> require([ 'dev/module' ]); </script>
2021-04-06 15:37:39

require.js data-main 上过期缓存的启发,我们使用以下 ant 任务更新了部署脚本:

<target name="deployWebsite">
    <untar src="${temp.dir}/website.tar.gz" dest="${website.dir}" compression="gzip" />       
    <!-- fetch latest buildNumber from build agent -->
    <replace file="${website.dir}/js/main.js" token="@Revision@" value="${buildNumber}" />
</target>

main.js 的开头是这样的:

require.config({
    baseUrl: '/js',
    urlArgs: 'bust=@Revision@',
    ...
});

生产中

urlArgs 会导致问题!

requirejs 的主要作者不喜欢使用urlArgs

对于已部署的资产,我更喜欢将整个构建的版本或哈希作为构建目录,然后只需修改baseUrl用于项目配置以使用该版本目录作为baseUrl. 然后没有其他文件更改,它有助于避免一些代理问题,即它们可能不会缓存带有查询字符串的 URL。

[造型我的。]

我遵循这个建议。

开发中

我更喜欢使用智能缓存可能经常更改的文件的服务器:一个在适当的时候用 304发出Last-Modified和响应的服务器If-Modified-Since即使是基于 Node 的express设置来提供静态文件的服务器也可以直接执行此操作。它不需要对我的浏览器做任何事情,也不会弄乱断点。

我们在生产系统中遇到了这个特定问题。我建议恢复文件名而不是使用参数。
2021-03-12 15:37:39
好点,但是,您的答案特定于您的服务器环境。对于遇到此问题的其他人来说,也许一个很好的替代方案是最近建议将版本号添加到文件名而不是查询字符串参数。以下是有关该主题的更多信息: stevesouders.com/blog/2008/08/23/...
2021-04-04 15:37:39