Prevent RequireJS from Caching Required Scripts
Inspired by Expire cache on require.js data-main we updated our deploy script with the following ant task:
<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:
require.config({
baseUrl: '/js',
urlArgs: 'bust=@Revision@',
...
});
Do not use urlArgs for this!
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.)
Serve your javascript assets with the proper HTTP headers to disable caching during development.
Using require's urlArgs means any breakpoints you set will not be preserved across refreshes; you end up needing to put debugger
statements everywhere in your code. Bad. I use urlArgs
for 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.
In development, I mock all ajax requests with a complex mockjax configuration, 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.
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.
I finally gave up and implemented my own fix directly into require.js. If you are willing to modify your version of the requirejs library, this solution might work for you.
You can see the patch here:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67
Once added, you can do something like this in your require config:
var require = {
baseUrl: "/scripts/",
cacheSuffix: ".buildNumber"
}
Use your build system or server environment to replace buildNumber
with a revision id / software version / favorite color.
Using require like this:
require(["myModule"], function() {
// no-op;
});
Will cause require to request this file:
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.
The patch will ignore any script that specifies a protocol, and it will not affect any non-JS files.
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:
var require = {
baseUrl: "/scripts/buildNumber."
};
I have not tried this, but the implication is that this would request the following 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
require.js - How can I set a version on required modules as part of the URL?
RequireJS can be configured to append a value to each of the script urls for cache busting.
From the RequireJS documentation (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.
Example, appending "v2" to all scripts:
require.config({
urlArgs: "bust=v2"
});
For development purposes, you can force RequireJS to bypass the cache by appending a timestamp:
require.config({
urlArgs: "bust=" + (new Date()).getTime()
});