A good[ish] website
Web development blog, loads of UI and JavaScript topics
This sequel post peeks under the hood of RequireJS optimization with r.js
and gulp
.
ℹ️ You might want to read the first part of the article.
As the first part dealt with the basics of RequireJS, this part looks more into the optimization of the created modules. After all, power is nothing without control.
r.js
is a tool to optimize RequireJS modules. It's an inherent part of RequireJS and maintained by the RequireJS team.
We all use Node all the time, right? Possibly in the form of Gulp or Grunt. r.js
exists as a Node module, we're gonna deal with Gulp here mostly, since it's pretty awesome. There is a gulp-requirejs module, but it's currently blacklisted with the message "use the require.js module directly". Which means that nothings gonna stop you from using it if you want, but in this article we use the official r.js
Node module.
There's also gulp-amd-optimize plugin, that adheres to Gulp's streaming philosophy, meaning it wont write anything to the filesystem, hence being a lot faster, I quite prefer that. It's just not as mature yet and has some limitations. I'm planning to write a post on that in the near future. (Actually there is many more r.js related AMD optimise modules out there in the wild).
Install via NPM:
$ npm install -g requirejs
R is a command line app, r.js
is the command and you configure it through a config file which you pass it as a param:
$ r.js -o build-config.js
Let’s look at the config file next.
You can see the example project repo I introduced in the first part here. Or view it via RawGit. Pop open dev tools and have look at the network tab. There's quite a buzz, that can be reduced to just two files with the optimizer.
There’s the build.js
file in src/js
with the following content:
;({
// If you got a main config file, this is the palce for it
mainConfigFile: 'config.js',
// Relative to this file's location
baseUrl: '.',
// The module name, this uses only a single file,
// there can be multiple different outputs also
name: 'config',
// The destination directory
out: '../../dist/js/global.js',
// If set to true, any files that were combined into a
// build bundle will be removed from the output folder.
removeCombined: true,
// Finds require() dependencies inside a require() or define call. By default
// this value is false, because those resources should be considered dynamic/runtime
// calls. However, for some optimization scenarios, it is desirable to
// include them in the build.
findNestedDependencies: true,
// For test purposes, the output is not minified.
// In the real world this would be set to uglify, uglify2, or to closure
optimize: 'none'
})
There are tons of more option, see all of them explained here.
Then, run the r.js
command from the command line:
$ r.js -o src/js/build.js
Tracing dependencies for: config
/Users/name/code/require-build-test/dist/js/global.js
----------------
/Users/name/code/require-build-test/src/lib/jquery.js
/Users/name/code/require-build-test/src/js/modules/dimensions.js
/Users/name/code/require-build-test/src/js/modules/classes.js
/Users/name/code/require-build-test/src/js/modules/sticky-position.js
/Users/name/code/require-build-test/src/js/modules/media-queries.js
/Users/name/code/require-build-test/src/js/modules/debounce.js
/Users/name/code/require-build-test/src/js/modules/hider-shower.js
/Users/name/code/require-build-test/src/js/modules/app.js
/Users/name/code/require-build-test/src/js/config.js
This will yield dist/js/global.js
file with all the modules in it. Next, the global.js
needs to be used instead of the modules:
<!-- Change this: -->
<script data-main="js/config" src="lib/require.js"></script>
<!-- To this: -->
<script data-main="../dist/js/global" src="lib/require.js"></script>
Now the network tab is much less busy:
gulp-shell can run command line commands from Gulp.
Install:
$ npm install --save-dev gulp-shell
Simple task definition in Gulpfile:
var gulp = require('gulp')
var shell = require('gulp-shell')
// Run the r.js command, such a simple task :)
gulp.task(
'scripts',
shell.task([
// This is the command
'r.js -o build/r/build.js'
])
)
Then add it to your default task:
gulp.task('build', ['concat', 'sass', 'cssopt', 'images', 'scripts'])
Or run as as it is $ gulp scripts
. For me it took around 2.2s to run, so it's not wise to put in watch, it makes developing a bit too much like walking tar.
All this niceness doesn't come for free, RequireJS creates a bit of overhead, even after the code optimisation, the module definitions are still in your files. And of course the RequireJS library needs to be loaded, it's 17kb as minified and 6kb in minified and gzipped. You can care of this or not, I don't, really, for now at least. But if you do, there are ways to deal with that.
AMD clean is helpful tool that turns your requireJS flavoured code to plain JavaScript. Then just load it like any old JS file. In this way, RequireJS can be used only as a development tool to organise code, and if the project happens to grows larger, the needed infrastructure is already there to accommodate larger volumes of code. I'm planning a post on this also.
Almond is somewhat like a lite version of RequireJS.
A replacement AMD loader for RequireJS. It provides a minimal AMD API footprint that includes loader plugin support. Only useful for built/bundled AMD modules, does not do dynamic loading.
See more in the Git repo.
RequireJS is a weird combination of a development tool and a front end script loader. At first it might be hard to see what's the benefit, but after using it a while it's hard to go back.
There will be a third part of these articles also, it's will delve deeper to the optimisation topics.
Comments would go here, but the commenting system isn’t ready yet, sorry.