303

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)的特定解决方案,请参阅下面的一些答案。

4

12 回答 12

461

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()
});
于 2011-12-12T19:46:58.427 回答
54

不要为此使用 urlArgs!

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

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

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

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

于 2013-02-25T22:56:50.713 回答
24

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

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

你可以在这里看到补丁:

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

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

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

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

像这样使用要求:

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

将导致要求请求此文件:

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

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

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

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

更新:

在拉取请求讨论中,requirejs 作者建议这可以作为修订号前缀的解决方案:

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

我没有尝试过,但这意味着这将请求以下 URL:

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

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

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

RequireJS 和代理缓存

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

于 2014-02-07T04:29:02.643 回答
19

受到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@',
    ...
});
于 2012-09-12T08:28:24.610 回答
11

生产中

urlArgs可能会导致问题!

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

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

[我的造型。]

我遵循这个建议。

开发中

我更喜欢使用智能缓存可能经常更改的文件的服务器:在适当的时候发出Last-Modified并响应If-Modified-Since304 的服务器。即使是基于 Node 的express set 来提供静态文件的服务器也能做到这一点。它不需要对我的浏览器做任何事情,也不会弄乱断点。

于 2013-11-01T16:09:12.910 回答
7

我从AskApache中获取了这个片段,并将其放入本地 Apache 网络服务器的单独 .conf 文件中(在我的例子中是 /etc/apache2/others/preventcaching.conf):

<FilesMatch "\.(html|htm|js|css)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>
</FilesMatch>

对于开发来说,这可以很好地工作,无需更改代码。至于制作,我可能会使用@dvtoever 的方法。

于 2013-12-09T20:00:30.533 回答
6

快速修复开发

对于开发,您可以在 Chrome 开发工具中禁用缓存(禁用 Chrome 缓存以进行网站开发)。仅当开发工具对话框打开时才会禁用缓存,因此您不必担心每次进行常规浏览时都切换此选项。

注意:使用 ' urlArgs ' 是生产中的正确解决方案,以便用户获得最新的代码。但这使调试变得困难,因为 chrome 每次刷新都会使断点无效(因为每次都提供一个“新”文件)。

于 2013-08-29T15:27:26.210 回答
3

我不建议使用 ' urlArgs ' 来使用 RequireJS 进行缓存突发。因为这并不能完全解决问题。更新版本号将导致下载所有资源,即使您只更改了单个资源。

为了解决这个问题,我建议使用像“filerev”这样的 Grunt 模块来创建修订号。最重要的是,我在 Gruntfile 中编写了一个自定义任务来更新修订版,无需任何地方。

如果需要,我可以共享此任务的代码片段。

于 2015-03-03T19:35:46.890 回答
2

这就是我在 Django / Flask 中的做法(可以很容易地适应其他语言 / VCS 系统):

在你的config.py(我在python3中使用它,所以你可能需要调整python2中的编码)

import subprocess
GIT_HASH = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8')

然后在您的模板中:

{% if config.DEBUG %}
     require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
    require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
  • 不需要手动构建过程
  • git rev-parse HEAD在应用启动时运行一次,并将其存储在config对象中
于 2014-11-26T14:14:02.463 回答
0

动态解决方案(没有 urlArgs)

这个问题有一个简单的解决方案,这样您就可以为每个模块加载一个唯一的修订号。

您可以保存原始 requirejs.load 函数,用您自己的函数覆盖它,然后再次将修改后的 url 解析为原始 requirejs.load :

var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
    url += "?v=" + oRevision[moduleId];
    load(context, moduleId, url);
};

在我们的构建过程中,我使用“gulp-rev”构建了一个清单文件,其中包含所有正在使用的模块的所有修订版。我的 gulp 任务的简化版本:

gulp.task('gulp-revision', function() {
    var sManifestFileName = 'revision.js';

    return gulp.src(aGulpPaths)
        .pipe(rev())
        .pipe(rev.manifest(sManifestFileName, {
        transformer: {
            stringify: function(a) {
                var oAssetHashes = {};

                for(var k in a) {
                    var key = (k.substr(0, k.length - 3));

                    var sHash = a[k].substr(a[k].indexOf(".") - 10, 10);
                    oAssetHashes[key] = sHash;
                }

                return "define([], function() { return " + JSON.stringify(oAssetHashes) + "; });"
            }
        }
    }))
    .pipe(gulp.dest('./'));
});

这将生成一个带有模块名称修订号的 AMD 模块,它包含在 main.js 中的“oRevision”中,您可以在其中覆盖 requirejs.load 函数,如前所示。

于 2016-10-11T12:19:23.440 回答
-2

这是@phil mccull 接受的答案的补充。

我使用他的方法,但我也通过创建要运行预构建的 T4 模板来自动化该过程。

预构建命令:

set textTemplatingPath="%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
if %textTemplatingPath%=="\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe" set textTemplatingPath="%CommonProgramFiles%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
%textTemplatingPath% "$(ProjectDir)CacheBuster.tt"

在此处输入图像描述

T4模板:

在此处输入图像描述

生成的文件: 在此处输入图像描述

在加载 require.config.js 之前存储在变量中: 在此处输入图像描述

require.config.js 中的参考:

在此处输入图像描述

于 2016-01-09T01:36:37.883 回答
-2

在我的情况下,我想在每次单击时加载相同的表单,我不希望我对文件所做的更改保持不变。它可能与这篇文章不完全相关,但这可能是客户端的潜在解决方案,无需为 require 设置配置。您可以制作所需文件的副本并保持实际文件完整,而不是直接发送内容。

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}
于 2017-03-21T17:52:16.920 回答