Javascript 防止 RequireJS 缓存所需的脚本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8315088/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 05:31:51  来源:igfitidea点击:

Prevent RequireJS from Caching Required Scripts

javascriptjqueryrequirejs

提问by BumbleB2na

RequireJS seems to do something internally that caches required javascript files. If I make a change to one of the required files, I have to rename the file in order for the changes to be applied.

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

The common trick of appending a version number as a querystring param to the end of the filename does not work with requirejs <script src="jsfile.js?v2"></script>

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

What I am looking for is a way to prevent this internal cacheing of RequireJS required scripts without having to rename my script files every time they are updated.

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

Cross-Platform Solution:

跨平台解决方案:

I am now using urlArgs: "bust=" + (new Date()).getTime()for automatic cache-busting during development and urlArgs: "bust=v2"for production where I increment the hard-coded version num after rolling out an updated required script.

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

Note:

笔记:

@Dustin Getz mentioned in a recent answer that Chrome Developer Tools will drop breakpoints during debugging when Javascript files are continuously refreshed like this. One workaround is to write debugger;in code to trigger a breakpoint in most Javascript debuggers.

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

Server-Specific Solutions:

服务器特定的解决方案:

For specific solutions that may work better for your server environment such as Node or Apache, see some of the answers below.

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

回答by Phil McCullick

RequireJS can be configured to append a value to each of the script urls for cache busting.

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

From the RequireJS documentation (http://requirejs.org/docs/api.html#config):

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

urlArgs: Extra query string arguments appended to URLs that RequireJS uses to fetch resources. Most useful to cache bust when the browser or server is not configured correctly.

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

Example, appending "v2" to all scripts:

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

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

For development purposes, you can force RequireJS to bypass the cache by appending a timestamp:

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

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

回答by Dustin Getz

Do not use urlArgs for this!

不要为此使用 urlArgs!

Require script loads respect http caching headers. (Scripts are loaded with a dynamically inserted <script>, which means the request looks just like any old asset getting loaded.)

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

Serve your javascript assets with the proper HTTP headers to disable caching during development.

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

Using require's urlArgs means any breakpoints you set will not be preserved across refreshes; you end up needing to put debuggerstatements everywhere in your code. Bad. I use urlArgsfor cache-busting assets during production upgrades with the git sha; then I can set my assets to be cached forever and be guaranteed to never have stale assets.

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

In development, I mock all ajax requests with a complex mockjaxconfiguration, then I can serve my app in javascript-only mode with a 10 line python http server with all caching turned off. This has scaled up for me to a quite large "enterprisey" application with hundreds of restful webservice endpoints. We even have a contracted designer who can work with our real production codebase without giving him access to our backend code.

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

回答by JBCP

The urlArgs solution has problems. Unfortunately you cannot control all proxy servers that might be between you and your user's web browser. Some of these proxy servers can be unfortunately configured to ignore URL parameters when caching files. If this happens, the wrong version of your JS file will be delivered to your user.

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

I finally gave up and implemented my own fixdirectly into require.js. If you are willing to modify your version of the requirejs library, this solution might work for you.

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

You can see the patch here:

你可以在这里看到补丁:

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

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

Once added, you can do something like this in your require config:

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

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

Use your build system or server environment to replace buildNumberwith a revision id / software version / favorite color.

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

Using require like this:

像这样使用 require:

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

Will cause require to request this file:

会导致 require 请求这个文件:

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

On our server environment, we use url rewrite rules to strip out the buildNumber, and serve the correct JS file. This way we don't actually have to worry about renaming all of our JS files.

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

The patch will ignore any script that specifies a protocol, and it will not affect any non-JS files.

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

This works well for my environment, but I realize some users would prefer a prefix rather than a suffix, it should be easy to modify my commit to suit your needs.

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

Update:

更新:

In the pull request discussion, the requirejs author suggest this might work as a solution to prefix the revision number:

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

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

I have not tried this, but the implication is that this would request the following URL:

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

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

Which might work very well for many people who can use a prefix.

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

Here are some possible duplicate questions:

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

RequireJS and proxy caching

RequireJS 和代理缓存

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

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

回答by dvtoever

Inspired by Expire cache on require.js data-mainwe updated our deploy script with the following ant task:

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>

Where the beginning of main.js looks like:

main.js 的开头如下所示:

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

回答by Louis

In production

生产中

urlArgscan cause problems!

urlArgs会导致问题!

The principal author of requirejs prefers not to use urlArgs:

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

For deployed assets, I prefer to put the version or hash for the whole build as a build directory, then just modify the baseUrlconfig used for the project to use that versioned directory as the baseUrl. Then no other files change, and it helps avoid some proxy issues where they may not cache an URL with a query string on it.

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

[Styling mine.]

[造型我的。]

I follow this advice.

我遵循这个建议。

In development

开发中

I prefer to use a server that intelligently caches files that may change frequently: a server that emits Last-Modifiedand responds to If-Modified-Sincewith 304 when appropriate. Even a server based on Node's expressset to serve static files does this right out the box. It does not require doing anything to my browser, and does not mess up breakpoints.

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

回答by myrho

I took this snippet from AskApacheand put it into a seperate .conf file of my local Apache webserver (in my case /etc/apache2/others/preventcaching.conf):

我从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>

For development this works fine with no need to change the code. As for the production, I might use @dvtoever's approach.

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

回答by Deepak Joy

Quick Fix for Development

快速修复开发

For development, you could just disable the cache in Chrome Dev Tools(Disabling Chrome cache for website development). The cache disabling happens only if the dev tools dialog is open, so you need not worry about toggling this option every time you do regular browsing.

为了发展,你可以只禁用Chrome开发工具中的缓存禁用浏览器缓存网站开发)。缓存禁用仅在开发工具对话框打开时发生,因此您不必担心每次进行常规浏览时切换此选项。

Note: Using 'urlArgs' is the proper solution in production so that users get the latest code. But it makes debugging difficult because chrome invalidates breakpoints with every refresh (because its a 'new' file being served each time).

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

回答by Amit Sagar

I don't recommend using 'urlArgs' for cache bursting with RequireJS. As this does not solves the problem fully. Updating a version no will result in downloading all the resources, even though you have just changes a single resource.

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

To handle this issue i recommend using Grunt modules like 'filerev' for creating revision no. On top of this i have written a custom task in Gruntfile to update the revision no wherever required.

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

If needed i can share the code snippet for this task.

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

回答by Stephen Fuhry

This is how I do it in Django / Flask (can be easily adapted to other languages / VCS systems):

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

In your config.py(I use this in python3, so you may need to tweak the encoding in python2)

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

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

Then in your template:

然后在您的模板中:

{% if config.DEBUG %}
     require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
    require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
  • Doesn't require manual build process
  • Only runs git rev-parse HEADonce when the app starts, and stores it in the configobject
  • 不需要手动构建过程
  • git rev-parse HEAD在应用程序启动时运行一次,并将其存储在config对象中

回答by mat

Dynamic solution (without urlArgs)

动态解决方案(无 urlArgs)

There is a simple solution for this problem, so that you can load a unique revision number for every module.

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

You can save the original requirejs.load function, overwrite it with your own function and parse your modified url to the original requirejs.load again:

你可以保存原来的 requirejs.load 函数,用你自己的函数覆盖它,再把你修改后的 url 解析成原来的 requirejs.load :

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

In our building process I used "gulp-rev" to build a manifest file with all revision of all modules which are beeing used. Simplified version of my gulp task:

在我们的构建过程中,我使用“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('./'));
});

this will generate an AMD-module with revision numbers to moduleNames, which is included as 'oRevision' in the main.js, where you overwrite the requirejs.load function as shown before.

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