阻止RequireJScaching所需的脚本
RequireJS似乎在内部做一些caching所需的JavaScript文件。 如果我对所需文件中的一个进行了更改,则必须重命名该文件才能应用更改。
追加版本号作为查询string参数到文件名末尾的常见技巧不能用于requirejs <script src="jsfile.js?v2"></script>
我正在寻找的是防止这个RequireJS所需脚本的内部caching,而不必在每次更新时重命名我的脚本文件的方法。
跨平台解决scheme:
我现在在开发过程中使用urlArgs: "bust=" + (new Date()).getTime()
用于自动caching清除, urlArgs: "bust=v2"
用于生产的urlArgs: "bust=v2"
,其中我在推出后递增硬编码版本num一个更新的必需脚本。
注意:
@Dustin Getz最近在回答中提到Chrome开发者工具将在debugging过程中抛弃断点,当Javascript文件不断刷新。 一种解决方法是编写debugger;
在代码中触发大多数JavaScriptdebugging器中的断点。
服务器特定解决scheme:
对于适合您的服务器环境(如Node或Apache)的特定解决scheme,请参阅下面的一些答案。
可以将RequireJSconfiguration为将值附加到每个脚本URL以caching清除。
从RequireJS文档( http://requirejs.org/docs/api.html#config ):
urlArgs :额外的查询string参数追加到RequireJS用来获取资源的URL。 当浏览器或服务器configuration不正确时,最有用的方法是caching。
例如,将“v2”附加到所有脚本中:
require.config({ urlArgs: "bust=v2" });
出于开发目的,您可以强制RequireJS通过附加时间戳来绕过caching:
require.config({ urlArgs: "bust=" + (new Date()).getTime() });
不要为此使用urlArgs!
要求脚本加载尊重HTTPcaching头。 (脚本加载了一个dynamic插入的<script>
,这意味着请求看起来就像任何旧的资产被加载。
在正确的HTTP标头中为您的JavaScript资源提供服务,以在开发期间禁用caching。
使用require的urlArgs意味着您设置的任何断点在刷新时不会被保留; 您最终需要在代码中的任何位置放置debugger
语句。 坏。 在生产升级过程中,我使用urlArgs
作为caching清除资产, 那么我可以设置我的资产永久caching,并保证永远不会有陈旧的资产。
在开发中,我用一个复杂的mockjaxconfiguration来模拟所有的ajax请求,然后我可以用一个10行的python http服务器来closures所有的caching,以javascript-only模式提供我的应用程序。 这已经扩展到一个相当大的“企业”应用程序与数百个宁静的web服务端点。 我们甚至有一个签约的devise师,可以使用我们真实的生产代码库,而不必让他访问我们的后端代码。
urlArgs解决scheme有问题。 不幸的是,你不能控制可能在你和用户的networking浏览器之间的所有代理服务器。 其中一些代理服务器可能会被configuration为在caching文件时忽略URL参数。 如果发生这种情况,您的JS文件的错误版本将被传递给您的用户。
我终于放弃了,并直接将我自己的修复程序实施到require.js中。 如果你愿意修改你的requirejs库的版本,这个解决scheme可能适合你。
你可以在这里看到补丁:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67
一旦添加,你可以在你需要的configuration中做这样的事情:
var require = { baseUrl: "/scripts/", cacheSuffix: ".buildNumber" }
使用您的构build系统或服务器环境来使用修订版本ID /软件版本/最喜欢的颜色replacebuildNumber
。
使用这样的要求:
require(["myModule"], function() { // no-op; });
将导致需要请求此文件:
http://yourserver.com/scripts/myModule.buildNumber.js
在我们的服务器环境中,我们使用url重写规则去除buildNumber,并提供正确的JS文件。 这样我们实际上不必担心重命名我们所有的JS文件。
该补丁将忽略任何指定协议的脚本,并且不会影响任何非JS文件。
这适用于我的环境,但是我意识到有些用户更喜欢前缀而不是后缀,应该很容易修改我的提交以适合您的需要。
更新:
在拉请求的讨论中,requirejs作者build议这可以作为一个解决scheme来修改数字的前缀:
var require = { baseUrl: "/scripts/buildNumber." };
我没有试过这个,但其含义是,这将要求以下url:
http://yourserver.com/scripts/buildNumber.myModule.js
对于许多可以使用前缀的人来说,这可能效果很好。
这里有一些可能的重复问题:
RequireJS和代理caching
require.js – 如何在需要的模块上设置一个版本作为URL的一部分?
受到require.js data-main上Expirecaching的启发,我们使用以下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
:
对于已部署的资产,我倾向于将整个构build的版本或散列作为构build目录,然后修改用于该项目的
baseUrl
configuration,以使用该版本化的目录作为baseUrl
。 然后没有其他文件改变,它有助于避免一些代理问题,他们可能不caching一个URL上的查询string。
[造型我的。]
我遵循这个build议。
开发中
我更喜欢使用智能caching可能经常更改的文件的服务器:发出Last-Modified
并响应If-Modified-Since
的服务器,并在适当的时候使用304。 即使是一个基于Node的Express服务器来提供静态文件的服务器也是如此。 它不需要对我的浏览器做任何事情,也不会弄乱断点。
我把这个片段从AskApache中拿出来放到我的本地Apache web服务器(在我的例子中是/etc/apache2/others/preventcaching.conf)的一个单独的.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开发工具 ( 禁用Chromecaching进行网站开发 )中禁用caching 。 caching禁用只有在开发工具对话框打开的情况下才会发生,所以您不必担心在每次定期浏览时切换此选项。
注意:使用' urlArgs '是生产中的正确解决scheme,以便用户获得最新的代码。 但是它使debugging变得困难,因为chrome每次刷新都会使断点无效(因为每次都有一个“新”文件被提供)。
我不build议使用' urlArgs '作为RequireJS的caching突发。 因为这不能完全解决问题。 更新版本号将导致下载所有资源,即使您只是更改了单个资源。
为了处理这个问题,我build议使用像“filerev”这样的Grunt模块来创build版本号。 在此之上,我已经写了一个自定义任务Gruntfile来更新修订号码任何地方需要。
如果需要,我可以分享这个任务的代码片段。
这是我在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 %}
- 不需要手动构build过程
- 只有在应用程序启动时才运行
git rev-parse HEAD
,并将其存储在config
对象中
这个问题有一个简单的解决scheme,所以你可以为每个模块加载唯一的版本号。
你可以保存原始的requirejs.load函数,用你自己的函数覆盖它,并再次将你的修改后的urlparsing为原始的requirejs.load:
var load = requirejs.load; requirejs.load = function (context, moduleId, url) { url += "?v=" + oRevision[moduleId]; load(context, moduleId, url); };
在我们的构build过程中,我使用“gulp-rev”来构build一个清单文件,其中包含所有正在使用的模块的所有修订版本。 我的大嘴巴任务的简化版本:
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函数。
嗯,那requirejs.undef
呢?
https://groups.google.com/forum/#!topic/requirejs/gq4bX8u6lPU
来自James Burke本人,RequireJS的作者:
确保使用requirejs 2.0,然后在重新定义mod2之前使用requirejs.undef()函数取消定义mod2:
http://requirejs.org/docs/api.html#undef
詹姆士
这是@ phil mccull接受的答案。
我使用他的方法,但我也通过创build一个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加载之前存储在variables中:
在require.config.js中引用:
在我的情况下,我想每次点击时加载相同的表单,我不希望我对文件所做的更改保持不变。 这可能与这篇文章无关,但是这可能是客户端的一个潜在的解决scheme,而不需要设置configuration。 不要直接发送内容,您可以制作所需文件的副本,并保持实际文件不变。
LoadFile(filePath){ const file = require(filePath); const result = angular.copy(file); return result; }