Versioned asset paths with RequireJS
While we were using RequireJS in a project we stumbled across a few problems. One of those problems was getting our assets loaded through a versioned path.
Each iteration we upgrade our asset version so the cache is busted after a release, to do this we simply set a parameter in
config.yml in symfony.
The desired result would be an url like this
http://hostname/v1/assets/js/vendor/bootstrap/dist/js/bootstrap.min.js. We use grunt to prepare and copy all third party libraries so we don't clutter the web folder. A small entry in your nginx vhost configuration file is needed in order to convert
This will point to the real location where our assets can be found:
<project>/web/assets/js/vendor/bootstrap/... So far so good, our nginx can understand the asset version and we have a parameter in symfony that can be modified when we need to bust the cache.
RequireJS and Twig
We have a separate twig template to setup requirejs, this allows us to load a specific module on a page without repeating ourself. More on that later on. This has several changes to it that differ from a basic setup as given by example in the requirejs docs.
After loading the initial
require.js file, the main configuration is loaded that resides in
./js/common. When this is loaded all following require statements are given, this will ensure that the configuration is loaded before any other module calls require on a library/module that it needs.
When working on larger projects, a setup like this might not be desirable. You might include a
_requirejs.html.twig multiple times.
The main problem with using multiple require calls is that RequireJS loads files asynchronously. As stated in the docs: http://requirejs.org/docs/api.html#data-main RequireJS does not guaranty that your configuration file (
common.js in this case) will be loaded before any other require calls are executed.
config.js file which is then loaded into the page using a separate
<script> tag. We can inject any variable into the global scope before loading the configuration file so we can set the base path there.
This is our requirejs template in twig that will load all files in the following order:
- set variables
- load config.js (this uses the variables set in the previous step)
- load all the remaining modules you require.
The config.js file will look like this:
If you are not using
r.js optimizer you now have versioned assets in project.
A full example can be seen below along with part of our
config.js file in the next section.
We use the optimizer to prepare our entire RequireJS setup. When injecting variables into the config.js you wil get the following error while compiling:
There is a simple, although dirty, workaround for this. The optimizer only reads the first
require.config tag, so all you have to do is append a second config tag below and everything will compile normally.
Now you are all setup to use versioned assets in your project with RequireJS.
Our optimizer config
As a bonus here is our grunt r.js optimizer file, it has some pathing setup because all the source files, including third party libraries are stored outside the webroot.