防止RequireJS缓存所需的脚本

时间:2011-11-29 17:30:30

标签: javascript jquery requirejs

RequireJS似乎在内部执行缓存所需javascript文件的操作。如果我对其中一个必需文件进行了更改,则必须重命名该文件才能应用更改。

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

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

跨平台解决方案:

我现在正在使用urlArgs: "bust=" + (new Date()).getTime()在开发过程中自动缓存清除,而urlArgs: "bust=v2"用于生产,我在推出更新的必需脚本后增加硬编码版本号。

注意:

@Dustin Getz在最近的回答中提到Chrome Developer Tools会在调试期间丢弃断点,此时Javascript文件会不断刷新。一种解决方法是在代码中编写debugger;以在大多数Javascript调试器中触发断点。

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

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

12 个答案:

答案 0 :(得分:447)

RequireJS可以配置为每个脚本URL附加一个值以进行缓存清除。

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

  

urlArgs :追加到RequireJS的网址的额外查询字符串参数   用于获取资源。最有用的是在浏览器时缓存半身像或   服务器配置不正确。

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

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

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

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

答案 1 :(得分:54)

不要使用urlArgs!

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

使用正确的HTTP标头为您的javascript资源提供服务,以便在开发过程中禁用缓存。

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

在开发中,我使用复杂的mockjax配置模拟所有ajax请求,然后我可以使用10 line python http server with all caching turned off以仅javascript模式提供我的应用。这已经扩展到了一个包含数百个宁静的Web服务端点的相当大的“企业”应用程序。我们甚至有一个签约设计师,他可以使用我们真实的生产代码库,而无需让他访问我们的后端代码。

答案 2 :(得分:22)

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

我终于放弃了implemented my own fix直接进入require.js。如果您愿意修改requirejs库的版本,则此解决方案可能适合您。

你可以在这里看到补丁:

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

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

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."
};

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

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

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

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

RequireJS and proxy caching

require.js - How can I set a version on required modules as part of the URL?

答案 3 :(得分:19)

Expire cache on 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@',
    ...
});

答案 4 :(得分:11)

在生产中

urlArgs会导致问题!

requirejs prefers not to use urlArgs的主要作者:

  

对于已部署的资产,我更喜欢为整体设置版本或哈希   构建为构建目录,然后只修改使用的baseUrl配置   让项目将该版本化目录用作baseUrl。然后   没有其他文件发生变化,它有助于避免某些代理问题   可能无法缓存带有查询字符串的URL。

[造型我的。]

我遵循这个建议。

在开发中

我更喜欢使用智能缓存可能经常更改的文件的服务器:发出Last-Modified的服务器,并在适当时使用304响应If-Modified-Since。即使是基于Node express设置为服务静态文件的服务器,也可以立即使用。它不需要对我的浏览器做任何事情,也不会破坏断点。

答案 5 :(得分: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的方法。

答案 6 :(得分:5)

快速修复开发

对于开发,您可以在Chrome开发工具中禁用缓存Disabling Chrome cache for website development)。仅当开发工具对话框处于打开状态时才会发生缓存禁用,因此您无需担心每次进行常规浏览时都会切换此选项。

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

答案 7 :(得分:3)

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

为了解决这个问题,我建议使用像'filerev'这样的Grunt模块来创建版本号。除此之外,我在Gruntfile中编写了一个自定义任务,以便在需要的地方更新版本号。

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

答案 8 :(得分: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对象

答案 9 :(得分: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模块到moduleNames,它在main.js中包含为'oRevision',在那里你覆盖requirejs.load函数,如前所示。

答案 10 :(得分:-1)

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

生成的文件: enter image description here

在加载require.config.js之前存储在变量中: enter image description here

require.config.js中的引用:

enter image description here

答案 11 :(得分:-2)

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

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