防止RequireJS缓存所需的脚本

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

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

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

跨平台解决方案:

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

注意:

@Dustin Getz在最近的回答中提到,当Javascript文件不断刷新时,Chrome开发工具将在调试过程中删除断点。一个解决方法是在代码中编写debugger;来触发大多数Javascript调试器中的断点。

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

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

111857 次浏览

RequireJS可以配置为每个脚本url附加一个值,用于缓存破坏。

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

urlArgs:附加到RequireJS的url的额外查询字符串参数 用于获取资源。最有用的缓存崩溃时,浏览器或 服务器配置不正确。< / p >

示例:将"v2"附加到所有脚本:

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

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

require.config({
urlArgs: "bust=" + (new Date()).getTime()
});

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 !

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

在开发过程中使用适当的HTTP头来禁用缓存。

使用require的urlArgs意味着你设置的任何断点在刷新时都不会被保留;你最终需要在你的代码中到处放置debugger语句。坏的。我使用urlArgs在git sha的生产升级期间用于缓存破坏资产;那么我就可以把我的资产设置为永远缓存,并保证永远不会有过时的资产。

在开发中,我用一个复杂的mockjax配置模拟所有ajax请求,然后我可以用10行python HTTP服务器,所有缓存关闭在仅javascript模式下服务我的应用程序。对我来说,这已经扩展到一个相当大的“企业级”应用程序,拥有数百个restful webservice端点。我们甚至有一个签约设计师,他可以使用我们真实的产品代码库,而不允许他访问我们的后端代码。

快速解决开发问题

对于开发,你可以只在Chrome开发工具中禁用缓存 (为网站开发禁用Chrome缓存)。缓存禁用只发生在开发工具对话框打开时,所以您不必担心在每次常规浏览时切换此选项。

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

在生产中

urlArgs会导致问题!

require 不喜欢使用urlArgs的主要作者:

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

(样式我的。)

我遵循这个建议。

在开发中

我更喜欢使用智能缓存可能频繁更改的文件的服务器:发出Last-Modified并在适当的时候用304响应If-Modified-Since的服务器。即使是基于Node的表达集来提供静态文件的服务器也会立即这样做。它不需要对浏览器做任何操作,也不会弄乱断点。

我从AskApache中取出这个片段,并将其放入本地Apache web服务器的一个单独的.conf文件中(在我的情况下是/etc/apache2/ others/preventcache .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的方法。

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;
});

将导致需要请求此文件:

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 and proxy caching

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

这是我在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对象中

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

为了解决这个问题,我建议使用Grunt模块,如“filerev”来创建修订号。在此之上,我已经在Gruntfile中编写了一个自定义任务,以在任何需要的地方更新修订。

如果需要,我可以分享这个任务的代码片段。

这是对@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"

enter image description here

T4模板:

enter image description here

< p >生成文件: enter image description here < / p > 在require.config.js加载之前存储在变量中: enter image description here < / p >

require.config.js中的引用:

enter image description here

动态解决方案(不含urlArgs)

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

您可以保存原始需求。加载函数,用自己的函数覆盖它,并将修改后的url解析为原始需求。负载:

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('./'));
});

这将生成一个带有修订号到moduleNames的amd模块,它被包含在main.js中的'oRevision'中,在那里你可以覆盖requirej。加载函数如前所示。

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

LoadFile(filePath){
const file = require(filePath);
const result = angular.copy(file);
return result;
}