防止Requirejs缓存所需的脚本
-
25-10-2019 - |
题
requirejs似乎在内部做一些缓存需要JavaScript文件的事情。如果我更改所需的文件之一,则必须重命名该文件才能应用更改。
将版本编号作为Querystring参数附加到文件名末尾的常见技巧不适用于requirejs <script src="jsfile.js?v2"></script>
我正在寻找的是一种防止requirejs必需脚本的内部缓存的方法,而无需每次更新我的脚本文件。
跨平台解决方案:
我现在正在使用 urlArgs: "bust=" + (new Date()).getTime()
用于开发过程中自动缓存和 urlArgs: "bust=v2"
对于生产,在推出更新的必需脚本后,我会增加硬编码版本的数字。
笔记:
@Dustin Getz在最近的答案中提到,Chrome Developer工具将在JavaScript文件不断刷新时在调试期间放置断点。一个解决方法是写 debugger;
在大多数JavaScript调试者中触发断点的代码中。
服务器特定的解决方案:
有关可能对您的服务器环境(例如节点或Apache)更有效的特定解决方案,请参见下面的一些答案。
解决方案
可以配置requirejs,以将每个脚本URL的值附加到缓存破坏的脚本URL。
从requirejs文档(http://requirejs.org/docs/api.html#config):
urlargs: :额外的查询字符串参数附加到requirejs用来获取资源的URL上。当浏览器或服务器未正确配置时,最有用的可用于缓存爆破。
例如,将“ V2”附加到所有脚本上:
require.config({
urlArgs: "bust=v2"
});
出于开发目的,您可以强制启用requirejs通过附加时间戳绕过缓存:
require.config({
urlArgs: "bust=" + (new Date()).getTime()
});
其他提示
不要为此使用urlargs!
需要脚本负载尊重HTTP缓存标头。 (脚本装有动态插入 <script>
, ,这意味着该请求看起来就像加载的任何旧资产一样。)
将您的JavaScript资产提供适当的HTTP标头,以在开发过程中禁用缓存。
使用requient的urlargs意味着您设置的任何断点都不会在刷新过程中保存;你最终需要放 debugger
您的代码中到处都是语句。坏的。我用 urlArgs
用于使用Git SHA生产升级期间的缓存资产;然后,我可以将自己的资产永久缓存,并保证永远不会拥有陈旧的资产。
在开发中,我用复杂的 无知 配置,然后我可以在仅JavaScript模式下使用我的应用 10行Python HTTP服务器都关闭了所有缓存. 。这为我扩展到了一个很大的“企业”应用程序,并具有数百个Restful Web服务端点。我们甚至有一个合同的设计师,他可以与我们的真实生产代码库一起工作,而无需让他访问我们的后端代码。
Urlargs解决方案有问题。不幸的是,您无法控制您和用户网络浏览器之间可能使用的所有代理服务器。不幸的是,这些代理服务器中的一些可以忽略在缓存文件时忽略URL参数。如果发生这种情况,则JS文件的错误版本将交付给您的用户。
我终于放弃了 实施了我自己的修复 直接进入require.js。如果您愿意修改requirejs库的版本,则此解决方案可能对您有用。
您可以在这里看到补丁:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eeeee631Ce68Eeeeeeeeeeeeeee09a5a67
添加后,您可以在您的需求配置中执行类似的操作:
var require = {
baseUrl: "/scripts/",
cacheSuffix: ".buildNumber"
}
使用您的构建系统或服务器环境替换 buildNumber
带有修订ID /软件版本 /喜欢的颜色。
使用这样的需要:
require(["myModule"], function() {
// no-op;
});
将导致要求请求此文件:
http://yourserver.com/scripts/myModule.buildNumber.js
在我们的服务器环境上,我们使用URL重写规则来剥离构建名称,并提供正确的JS文件。这样,我们实际上不必担心重命名所有JS文件。
该补丁将忽略任何指定协议的脚本,并且不会影响任何非JS文件。
这适合我的环境,但是我意识到有些用户更喜欢前缀而不是后缀,应该很容易修改我的承诺以满足您的需求。
更新:
在拉的请求讨论中,requirejs作者建议这可以作为解决方案的解决方案。
var require = {
baseUrl: "/scripts/buildNumber."
};
我没有尝试过,但是这意味着这会请求以下URL:
http://yourserver.com/scripts/buildNumber.myModule.js
对于许多可以使用前缀的人来说,这可能很好。
这是一些可能的重复问题:
受到启发 在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缓存URL。
造型地雷。
我遵循这个建议。
开发中
我更喜欢使用智能缓存可能经常更改的文件的服务器:发射的服务器 Last-Modified
并回应 If-Modified-Since
在适当的情况下使用304。甚至基于节点的服务器 表示 设置为静态文件提供的功能将其直接解决。它不需要对我的浏览器做任何事情,也不会弄乱断点。
我从 askapache 并将其放入我的本地Apache Web服务器的单独.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的方法。
快速修复开发
为了开发,您可以 禁用Chrome Dev工具中的缓存 (禁用网站开发的Chrome Cache)。缓存禁用仅在打开“开发工具对话框”时才发生,因此您不必担心每次定期浏览时都会切换此选项。
注意:使用'urlargs'是生产中适当的解决方案,因此用户获得了最新的代码。但这使调试变得困难,因为Chrome无效每个刷新(因为它每次都会提供一个“新”文件)。
我不建议使用'urlargs'对于用requirejs爆发的缓存。因为这不能完全解决问题。更新版本否将导致下载所有资源,即使您只需更改单个资源。
为了处理此问题,我建议使用诸如“ Filerev”之类的grunt模块来创建修订版。最重要的是,我已经在Gruntfile中编写了一个自定义任务,以更新任何需要的修订。
如果需要,我可以为此任务共享代码段。
这就是我在django /烧瓶中进行操作的方式(很容易适应其他语言 / 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.s.load功能,用自己的功能覆盖它,然后将修改后的URL解析为原始requirejs.s.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”包含在其中,您可以在其中覆盖requienjs.s.load函数,如前所述。
这是@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:
就我而言,每次点击时,我都想加载相同的表格,我不希望我在文件停留上所做的更改。它可能与此帖子完全无关,但这可能是客户端的潜在解决方案,而无需设置Conforion。您可以制作所需文件的副本并保持实际文件完整,而不是直接发送内容。
LoadFile(filePath){
const file = require(filePath);
const result = angular.copy(file);
return result;
}