RequireJS Optimizer Example
1. RequireJS
RequireJS is an AMD JavaScript library that supports asynchronous file and module loading. It is optimized for web browser usage but it is also possible to use in Java or Node environments. It is compatible with all main browsers and quite intuitive to use. Its main benefit is that it offers the option to logically structure an application following the AMD principles.
In the article https://www.webcodegeeks.com/javascript/requirejs/requirejs-tutorial-how-to-use-requirejs/ you can find more information about how to use RequireJS.
In this article we are going to explain how to build and optimize the deliverables that we want to ship into production. This task should be launched as part of the build and release process once the development is finished and things are ready to go live.
Table Of Contents
What we are going to do is to create a very simple application (just for learning purposes) containing several modules that are loaded using RequireJS, after that we are going to use the RequireJS optimizer for concatenating, minifying and uglyfying our deliverables so they are ready for release.
So before starting let’s create our application to start working with (at the bottom of this tutorial, in the download chapter, you can download all examples mentioned in the article).
Our main.js file containing the require.js configuration looks like the following:
main.js
//check download section to see HTML code requirejs.config({ baseUrl: 'js/shop', waitSeconds: 20, paths: { "jquery": "jquery", "backbone": "backbone", "underscore": "underscore" }, //for traditional libraries not using define() we need to use a shim that allows us to declare them as AMD modules shim: { "backbone": { "deps": ["underscore", "jquery"], "exports": "Backbone" //attaches "Backbone" to the window object }, "underscore": { exports: "_" // exporting _ } }, deps: ['app'], urlArgs: "t=20160320000000" //flusing cache, do not use in production }); });
The application entry point is called app.js and contains following code:
shop/app.js
//check download section to see HTML code define([ "jquery", "backbone", "underscore", "food", "travel", "clothes" ], function ($, Backbone, _, Food, Travel, Clothes) { 'use strict'; var clothe = new Clothes(), food = new Food(), travel = new Travel(); $(".shop-clothes-button").click(function(event) { $(".content").html(clothe.getClothes()); return false; }); $(".shop-food-button").click(function(event) { $(".content").html(food.getFoods()); return false; }); $(".shop-travel-button").click(function(event) { $(".content").html(travel.getDestinations()); return false; }); }); });
The next 3 snippets show our different modules, they do not have much content, just for learning purposes. Clothes.js:
shop/clothes.js
//check download section to see HTML code
define([
"backbone",
"underscore"
], function (Backbone, _) {
'use strict';
return Backbone.Model.extend(
{
getClothes: function () {
return "
";
}
});
});
});
Other module called food.js:
shop/food.js
//check download section to see HTML code
define([
"backbone",
"underscore"
], function (Backbone, _) {
'use strict';
return Backbone.Model.extend(
{
getFoods: function () {
return "
";
}
});
});
});
And other module called travel.js:
shop/travel.js
//check download section to see HTML code
define([
"backbone",
"underscore"
], function (Backbone, _) {
'use strict';
return Backbone.Model.extend(
{
getDestinations: function () {
return "
";
}
});
});
});
No rocket science until now. Other required modules in the application are require.js, jquery, backbone and underscore (we will show in this article how to load these modules directly from our application and also using CDN links).
Here are the styling files used in the application:
css/styles.css
//check download section to see HTML code @import url("food.css"); .shop-clothes-button{ color: blue; } /* comment to delete*/ .shop-travel-button{ color: green; }
And the imported styling file food.css:
css/food.css
//check download section to see HTML code /* comment to delete*/ .shop-food-button{ color: red; }
Finally here is the html file with the presentation logic:
index.html
//check download section to see HTML code <head lang="en"> <meta charset="UTF-8"> <title>Shop</title> <script src="js/require.js" data-main="js/main.js"> < /script> <link rel="stylesheet" href="css/styles.css"> </head> <body> <ul> <li > <input class="shop-clothes-button" type="button" value="Clothes" / > < / li > <li > <input class="shop-food-button" type="button" value="Food" /> < / li > <li > <input class="shop-travel-button" type="button" value="Travel" / > < /li > </ul> <div class="content"> </div> </body> </html>
Now our AMD application is ready and can be tested by opening in a browser the index.html page. The application itself is not interesting, what we need to check here is the files that have been downloaded in order to understand how RequireJS works. We can check that by opening the developer tools in Chrome or Internet Explorer or with any other plugin like Firebug in Firefox, I do not have preferences but I am going to use the Chrome dev tools. Well, we can see there that a total of 12 requests have been done in order to download all the required content including 9 JS files, 2 CSS files and 1 HTML file.
Now we are going to show how to change and optimize that.
2. RequireJS optimizer
RequireJS has an optimization tool that does the following:
- Combines related scripts together
- Minifies and uglyfies them
- Optimizes and combines CSS files
Only modules loaded via string literals can be optimized using the requireJS optimization tool. This means, modules loaded using variables in runtime cannot be part of the build process.
The optimizer can be run using NodeJS, Java or in the Browser, since NodeJS is the recommended tool for the RequireJS maintainers, we are going to use NodeJS in this article. So before you continue, please install NodeJS in your system: https://nodejs.org/en/download/.
Once Node is installed you can install the RequireJS optimization tool, the preferred way to do it is via nodeJS:
npm install -g requirejs
The optimization tool can be used via command line arguments or via configuration file, in this article we are going to use configuration files because I think is more clear and easy to understand. Just to mention that command line arguments take precedence over build profile settings, and you can mix them together.
3. One single JS file
In order to optimize and create a JS file that contains the content of all its dependencies we can use the following build profile:
buildOneFile.js
//check download section to see HTML code ({ baseUrl: "js/shop", paths: { "jquery": "jquery", "backbone": "backbone", "underscore": "underscore" }, shim: { "backbone": { "deps": ["underscore", "jquery"], "exports": "Backbone" //attaches "Backbone" to the window object }, "underscore": { exports: "_" // exporting _ } }, name: "../main", out: "../built/js/main.js" })
By running the following command:
node ../../r.js -o buildOneFile.js
We create a file named main.js containing all the dependencies for your app. This file can be used now by index.html to load all required JS files just with one request.
Generated files should not be saved in the directory where your source code is stored but in a copy of the project. In next chapters we will see that RequireJS can take care of copying all application files to a build directory automatically.
4. One single CSS file
As mentioned before, CSS files can also be optimizer using following parameters directly in the command line:
node ../../r.js -o cssIn=main.css out=main-built.css
or in a build file using the same properties:
... cssIn:"main.css", out:"main-built.css" ...
Both will create a file called appdirectory/css/main-build.css that will include the contents of main.css, have the url() paths properly adjusted, and have comments removed.
The optimization tool supports an argument called optimizeCss
. This argument controls the CSS optimization settings. Allowed values: “none”, “standard”, “standard.keepLines”, “standard.keepComments”, “standard.keepComments.keepLines”.
5. All project
The tool can optimize all CSS and JS files in an application and create a deliverable ready to ship to production by using a build profile. In our example we are going to create a build profile called build.js
that contains the following:
build.js
//check download section to see HTML code ({ baseUrl: "js/shop", appDir: '.', paths: { "jquery": "jquery", "backbone": "backbone", "underscore": "underscore" }, shim: { "backbone": { "deps": ["underscore", "jquery"], "exports": "Backbone" //attaches "Backbone" to the window object }, "underscore": { exports: "_" // exporting _ } }, optimizeCss: "standard.keepLines", modules: [ { name: "app" } ], dir: "../built" })
This build profile tells RequireJS to copy all app directory (appDir parameter) to the output directory built (dir parameter) and apply all optimizations to the files located in the output directory.
To run the build profile you just run the following command in the app directory:
node r.js -o build.js
If you open now the index.html file located in the built directory you can see the differences in the amount of the files downloaded (only 1 app related JS file and only 1 combined CSS without comments and spaces) and in the content of the files since these are now minified and uglified.
6. Usage of CDN paths
In case that you want to load scripts using a CDN (Content Delivery Network), then the optimizer cannot load them. So in case you want to include these scripts in your optimization build process and deliver them in the concated output files, you need to map these files to a module name and download the files to your local file path.
Normally this is not the case, if you are using a CDN for loading resources is because you want to download them directly from the external domain and allow applications to cache them independently to your application. In other words, you do not want to include these modules and their dependencies in the build.
In order to skip these files we can use the special word “empty” in the paths configuration as shown in the following build profile:
build.js
//check download section to see HTML code ({ baseUrl: "js", name: "mainCDN", out: "js/mainCDN-built.js", paths: { jquery: "empty:" } })
The main.js configuration file looks like the following:
main.js
//check download section to see HTML code requirejs.config({ paths: { 'jquery': 'https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9hamF4Lmdvb2dsZWFwaXMuY29tL2FqYXgvlibs/jquery/1.7.1/jquery.min' } }); require(['jquery'], function ($) { });
This example is also available in the download section of this article.
7. Source maps
The version 2.1.6 of the optimization tool and higher have experimental support for source maps. The unminified files will show up in the developer tools with a “.src.js” file extension.
In order to generate source maps, the parameter generateSourceMaps
needs to be set to true. It is also needed that the preserveLicenseComments
parameter is set to false, this is because the tool needs to have control and rights to modify files completely. For an explanation about source maps, what they are and how they work and can be used please visit
8. Configuration options
The RequireJS optimization tool supports several options, we are going to explain here the most important ones (from my point of view):
appDir
: If this option is specified all files inside the directory will be copied to the build directory and baseUrl will be relative to it. This option is optional.baseUrl
: All modules are loaded relatively to this path. As mentioned before, if appDir is specified, baseUrl is relative to appDir. If baseUrl is not specified, per default, modules are loaded relative to the directory where the build file is located.dir
: Directory where the output is going to be saved. Default is “build” directory relative to the build file location.name
: name of the single module to be optimized. This is useful if you are only interested in optimizing one single file.modules
: This property allows you to list all modules to be optimized.optimize
: This option specifies how files are to be optimized. Allowed values are “uglify”, “uglify2”, “none”, “closure”, “closure.keepLines”. Default is “uglify”.generateSourceMaps
: This option can be true or false and is only usable if the optimize option supports generation source maps. Source maps generate non minified JS from minified JS files.uglify2
: configuration options when using uglify2 as optimization modus.optimizeCss
: option to configure the CSS optimizations. Possible values are “standard”, “standard.keepLines”, “standard.keepComments”, “standard.keepComments.keepLines”, “standard.keepWhitespace”.cssIn
: used to optimize a single CSS file. Always used in combination with out.out
: Output of combied and optimized CSS file.include
: This property indicates that a module combination and optimization should include the files and its dependencies listed.exclude
: This property indicates that a module combination and optimization should exclude the files listed. When trying to exclude files from a module optimization is better to declare them as module for optimization before in your configuration file than to exclude them directly.
In order to check all available options please visit the following page:
9. Gulp NPM optimizer
It is also important to mention that the requireJS optimization can be automated using tools like Gulp via its RequireJS optimizer module. For more information about how to install and use this module please visit
10. Download
In the following link you can download a full working example covering the main components explained in this article: requireJSOptimizerExample
11. Links
The following links contain more information and references about the topics mentioned in this article:
- http://requirejs.org/docs/optimization.html
- http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/
- https://www.npmjs.com/package/gulp-requirejs-optimize/
- https://github.com/requirejs/r.js/blob/master/build/example.build.js#L27/