Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expire cache on require.js data-main

I'm using require.js and r.js to package my AMD modules. I'm using jquery & requirejs via the following syntax:

<script data-main="/js/client" src="/js/external/require-jquery.js"></script> 

This all works great pre & post packaging, but I run into issues a lot where chrome & mobile safari hold on to the cached version of client.js. I'd like to add a cachebuster to client.js, but I can't seem to figure out how to do it using the above syntax.

I tried some variations of:

<script data-main="js/client.js?b=busted" src="/js/external/require-jquery.js"></script> 

but now require tries to get client.js from /, not /js, so it 404s.

I also tried adding

urlArgs : "bust="+new Date().getTime() 

to require.config, but it appears to have no effect.

I also tried adding the same value to app.build.js, but when it's in there, r.js no longer concatenates my js files, just uglifies them.

What is the proper syntax to bust a require.js data-main script cache?

like image 741
Jesse Avatar asked Jun 18 '12 18:06

Jesse


People also ask

How does RequireJS work?

RequireJS uses Asynchronous Module Loading (AMD) for loading files. Each dependent module will start loading through asynchronous requests in the given order. Even though the file order is considered, we cannot guarantee that the first file is loaded before the second file due to the asynchronous nature.

Why do we need RequireJS?

RequireJS is a basic loader, which is used to loads the JavaScript files, it is a framework to manage dependencies between JavaScript files, and in modular programming, all the functionality divides in different modules, so RequireJs is a best tool to assemble different JavaScript files from different modules by which ...

What is Shim RequireJS?

shim: Configure the dependencies, exports, and custom initialization for older, traditional "browser globals" scripts that do not use define() to declare the dependencies and set a module value. Here is an example. It requires RequireJS 2.1. 0+, and assumes backbone. js, underscore.

Is RequireJS synchronous?

So, RequireJS doesn't support it. From your use case it seems that you don't need synchronous RequireJS, you need to return result asynchronously. AMD pattern allows to define dependencies and load them asynchronously, but module's factory function must return result synchronously.


1 Answers

How are you defining your require.config? I think for it to take effect before you import require.js, you need to code it like this:

<script type="text/javascript">     var require = {         baseUrl: "/scripts/",         waitSeconds: 15,         urlArgs : "bust="+new Date().getTime()     }; </script> <script data-main="app/main" src="/scripts/require.js"></script> 

Specifically, a an object named 'require' must be constructed before you import require.js.

UPDATE

As Jesse points out in the comments below, there are a few enhancements you should apply to your require{} object for production use. The above example is cribbed from the RequireJS documentation and modified as little as possible to answer this question.

Here are a few things to consider for production use:

  • Instead of using the current date-time as your cache-busting variable, you should use a build number from your development environment. This allows your clients to cache the Javascript between releases but will cause them to refresh their cache whenever you do a software update.
  • Jesse also uses the require{}'s ability to specify dependencies instead of using the data-main attribute of the script. I don't know if that is strictly better, but I think it is cleaner looking.
  • Adjust the waitSeconds based on your needs. I used the example value from the RequireJS documentation, but you should adjust the value or omit it, based on your needs.

So if you apply these techniques, your code might look like:

<script type="text/javascript">     var require = {         baseUrl: "/scripts/",         waitSeconds: 15,         urlArgs : "bust="+{{buildNumber}},         deps : ['app/main']     }; </script> <script src="/scripts/require.js?bust={{buildNumber}}"></script> 

Note, in this case {{buildNumber}} is a value supplied by the server.

UPDATE 2

The urlArgs cache bust 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 would recommend using a buildNumber in your Javascript filename request, like buildNumber.myModule.js (prefix) or myModule.buildNumber.js (postfix). You can use the prefix style by modifying the baseUrl:

baseUrl: "/scripts/buildNumber", 

Note the lack of a '/' at the end of the baseUrl.

You will need to use a modified version of require.js to use the postfix solution. You can read more about this here: https://stackoverflow.com/a/21619359/1017787

Obviously in either case you will want to use some solution to replace buildNumber with some type of version number that changes with each release.

like image 189
JBCP Avatar answered Sep 22 '22 07:09

JBCP