LazyJS
LazyJS is an alternative to CommonJS/Modules and RequireJS with a minimal design that allows you to make your own decisions, rather than trying to force them on you.
Concept
The idea with LazyJS is very similar to what RequireJS aims at, which is dependencies that resolve themselves. The difference is LazyJS aims to be less intrusive in how you should style your code, and lets you determine how to handle dependencies beyond the physical modules you use.
Getting Prepared
If you care about contributing, you can either download the code using git
, or through npm
.
git clone https://github.com/bevacqua/lazyjs.git
npm install lazyjs
LazyJS has a one-step build process powered by Grunt.
Use grunt demo
to see the concept in action, grunt build
to build, grunt tests
for tests.
Getting Started with LazyJS
Using LazyJS, you only need to embed a single <script>
tag in your HTML
<script id='lazyjs' src='/js/vendor/lazy.min.js' data-jumpstart='/js/app.js'></script>
This <script>
tag will allow LazyJS to load via AJAX the script you reference in the data-jumpstart
attribute. That's familiar enough, RequireJS behaves similarly.
Within scripts, though, there are differences. LazyJS uses what's called comment directives. There's two types of these directives.
-
define
directives, which allow us to define multiple modules in a single file:/*! lazy define $name */
/*! lazy define module-1 */ function sum(a, b){ return a + b; } /*! lazy define module-2 */ function print(text){ console.log(text); }
Definitions mean that your modules will now be identified by whatever name $name
you pick
-
require
directives, which make a module depend on another one:/*! lazy require $name */
/*! lazy define module-3 */ /*! lazy require module-1 */ /*! lazy require module-2 */ function funky(){ print('the result is ' + sum(5, -2)); }
Dependency, as defined by require
comment directives, means that the code in a module won't be evaluated at all until every single dependency has been evaluated and resolved.
Getting Practical
In a more practical scenario, you would probably want to completely skip module definitions, and stick to the one-module-per-file convention, where if you had a script at /js/foo.js
, you could add a require
directive like the one below.
/*! lazy require /js/foo.js */
If you stick to one module per file, define
directives are not necessary (during development). These do become crucial when you are bundling your scripts together, though.
Getting Real
In a production scenario you don't want all those AJAX requests flying around. So you change things up very subtly:
<script id='lazyjs' src='/js/vendor/lazy.min.js' data-jumpstart='/js/app.js' data-bundle='js/all.js'></script>
This might be kind of deceiving at first glance, but it will just perform two HTTP requests. One for LazyJS, and the other one for everything else. But here's the beauty.
Even though we've now fetched every single line of JavaScript, we are not going to evaluate it yet, we are just storing the strings, after some manipulation to figure out which module is which, and which module needs what. We don't even split the string until we need it.
This approach drastically increases average load times, and that's even when the script is cached and gzipped (as they should be). That's because JS parsing can take up a significant time on each page load. Time that might be better spent elsewhere, or better distributed.
Ok, but what happens to the jump-start script now? Well, it's essentially the same scenario as it was before using bundles, except now you have a nice hashtable with a list of modules that are eager to be evaluated. If /js/app.js
is among those scripts, it will be parsed and executed immediately, if it's not, then the usual AJAX request would go get it.
If an script has any number of dependencies, then those will be resolved, recursively, until all dependencies are resolved and the script can load. Then, everything gets evaluated.
Note that modules will only be fetched/parsed/evaluated once. Just like when you don't have the aid of a script loader.
Disclaimer
This is the early prototype of an idea, an experiment if you will. Hopefully I'll get some input on it's viability, and then decide where the project will go from there.
Suggestions are welcome!