Minification is important to load web applications faster. The scope of this research is to reduce the page load time by decreasing the number and size of the requests performed by the browser. The number of requests is decreased by combining multiple CSS or JavaScript files in a single file. The request size is reduced by removing white spaces and applying other compression algorithms that gives you smaller files while preserving the same functionality, e.g.: name mangling, stripping comments etc.
The minification will be performed as a predeployment step using a SDK command like rain minify which will generate the minified files for each component. It might be desired that this command generates a new RAIN project containing only the minified files instead of adding the minified files in the current project.
The tool used for JavaScript minification is RequireJS Optimizer. It is able to trace module dependencies and combine multiple RequireJs modules into a single file. When combining multiple modules in the same file each module must have the optional module name specified:
define("example/3.0/js/notes", ["js/note"], function (Note) {
function Notes () {}
return Notes;
});
When performing minification, the client-side controllers associated with each view are identified and added to the list of modules to be included in the minified file. The dependencies of each client-side controller are identified and included by the RequireJS Optimizer. This process generates a file named index.min.js for each component. Any external dependencies are excluded since it would be redundant to have the same module in multiple files. A sample configuration used to minify the javascript files for the example component looks like this:
var options = {
/*
* Path used to locate the modules.
* The module path is composed as baseUrl/{moduleName}.js
*/
baseUrl: '/path/to/rain/components/example/client',
/*
* minification options
* Name mangling is disabled since it will also change the names for t, nt and logger.
*/
optimize: "uglify2",
uglify2: {
mangle: false
},
"packages": [{
"name": "raintime",
"main": "raintime",
"location": "../../core/client/js"
}],
/*
* The modules to be included in the minified file.
* Dependencies are detected automatically by RequireJS Optimizer.
*/
include: ['js/index', 'js/notes', 'js/text_localization'],
/*
* Any external dependencies shouldn't be included; they are included in the file
* located in the component to which they belong.
* In this configuration only the raintime dependencies are excluded, but any external
* module should be added here.
*/
exclude: ["raintime"],
out: '/path/to/rain/components/example/client/js/index.min.js',
/*
* An empty module is added at the end of the file since the require for the minified
* file will look like: require(['example/3.0/js/index.min]', function () {});
*/
wrap: {
end: 'define('example/3.0/js/index.min', [], function () {});'
},
/*
* This function is called when the contents of a module are read. It is also called for
* excluded modules.
* It adds an empty define for modules that are loaded in the global scope like jquery.
* RequireJS Optimizer does this automatically, but this define isn't included in the
* contents passed to the onBuildWrite method where module names are replaced with the
* correct names for the RAIN context.
*/
onBuildRead: function (moduleName, path, contents) {
// global modules
if (contents.indexOf('define(') === -1) {
contents += '\n\n';
contents += util.format('define("%s", function(){});', moduleName);
contents += '\n\n';
}
return contents;
},
/*
* On build write is called before the contents of a module are added to the minified file.
* It replaces the module names with the correct ones, RequireJS Optimizer has no knowledge
* about RAIN's components and routes. For example js/notes becomes example/3.0/js/notes.
*/
onBuildWrite: (function (moduleName, path, contents) {
return contents.replace(moduleName, util.format('example/3.0/%s', moduleName));
}
};
After this, the RequireJS Optimizer is called with these options to perform the actual minification:
requirejs.optimize(options, function () {
// minification finished
});
An exception from the process described above is the core component. The configuration for the core component is written manually and it should look like this:
var options = {
baseUrl: path.join(coreComponentPath, 'client/js'),
optimize: "uglify2",
uglify2: {
mangle: false
},
"packages": [{
"name": "raintime",
"main": "raintime",
"location": "."
}],
/*
* These modules have all the other modules in the core component as dependencies.
* When adding modules to the core component you need to make sure that all the modules
* from the core component are still included in the minified file. Just putting all
* the files in the include list will not help since the modules should appear in a
* specific order to guarantee that module dependencies will be resolve correctly.
*/
include: [
'raintime/dependencies',
'raintime/client_rendering',
'raintime/dialog',
'raintime/translation'
],
out: path.join(coreComponentPath, 'client/js/index.min.js'),
wrap: {
end: "define('raintime/index.min', [], function () {});"
}
};
Some changes need to be implemented in RAIN to use the minified files:
- a flag indicating if minification is enabled should be added
- the dependencies module should be modified to support multiple modules per file
- raintime should be modified to require the index.min module associated with the component before requiring the client side controller when minification is used.
- the bootstrap should be modified to require the minified raintime.
The tool used for CSS minification is less. It supports CSS minification using the YUI compressor. All the css files (*.css) for a component are rendered using less and placed in the same file. The rules should also be counted when adding CSS to the minified file to avoid the situation where more than 4095 rules are added in the same file. If this is the case, a JSON file should be created specifying which files were added to which minified file.
Some changes needs to be implemented in RAIN to use the minified files:
- a flag indicating if minification is enabled should be added
- the css helper should be modified to replace the requested css file path with the minified file when minification is enabled. It should also ensure that the same path is added only once in the css list
- there is an issue when precompiling less at server startup that when multiple URLs are on the same line, only the first URL is rewritten
- media queries aren’t working properly in the current prototype because all the CSS files are placed in the same file and no mather which file will be matched by the media query selector the same css will be placed in the page.