Javascript Namespace Inclusion, loaders and all that…!
Intro: In its current specification, the Javascript language (ECMA-262) has no inherent facility for loading packages/modules other than the handy […]
27th Jun 2012
Javascript Namespace Inclusion, loaders and all that…!
Intro:
In its current specification, the Javascript language (ECMA-262) has no inherent facility for loading packages/modules other than the handy script tag. Developers had to make do with dynamically creating the script tag and inserting it into the DOM as shown below:
// Very simple asynchronous script-loader // Create a new script element var script = document.createElement('script'); // Find an existing script element on the page (usually the one this code is in) var firstScript = document.getElementsByTagName('script')[0]; // Set the location of the script script.src = "http://example.com/myscript.min.js"; // Inject with insertBefore to avoid appendChild errors firstScript.parentNode.insertBefore( script, firstScript ); // That's it!
Equivelants such an import, include or require statements present in the design of programming languages such as Java, C/C++, Ruby, Python etc. has enabled developers to prevent namespace pollution and organize their code in a modular fashion
promoting loose coupling and re-use of components in software projects.
So what are the advantages of namespaces:
- Architectural design pattern
- Organize files into shareable modules
- Create a maintainable and re-useable code base
- Improve load speed
- Makes code available to the current namespace
Asynchronous Module Definition (AMD)
AMD evolved as a mechanism for Javascript developers to develop modular Javascript code such that modules and their dependencies could be loaded asynchronously. This would be speed-up load times and avoid blocking or waiting of dependant script during the loading process.
So, as I was looking into developing an app using Ember.js, I was confronted with this simple problem – how to include JS function in files spread into different directories:
MyApp/ App.js index.html app/ assets/ css/ img/ models/ views/ controllers/ stores/
As I was pondering on this point…yah…I had a list of ideal features in mind that I needed and some nice to have’s:
Requirements/Features:
- Framework agnostic
- Local and remote loading eg. CDN repositories
- Dynamic loading
- Single/unique instance loading of script file
- Dependency management – load order
- Parallel or ‘Non-blocking’ loading ie. loading scripts that do not have dependencies
- Synchronous and Asynchronous loading
- Loading of assets/resource eg. HTML, CSS, images
- Track versioning
- File concatenation
….Googling and a few clicks….resulted in me compiling a short list of Javascript
loaders for future reference:
Resources:
- Ajile – http://ajile.net/
- RequireJS – http://requirejs.org/docs/api.html
- Inject – https://github.com/linkedin/inject
- jsLoad – http://code.google.com/p/jsload/
- Sprockets- https://github.com/sstephenson/sprockets – used at 37signals
- LabJS – http://labjs.com/ – used at Getify Solutions
- HeadJS – http://headjs.com/
- LoadRunner – https://github.com/danwrong/loadrunner
- Yahoo YUILoader – http://developer.yahoo.com/yui/yuiloader/ – Yahoo
- JQuery.atreq – https://github.com/phleet/jquery.atreq
- PyramidJS – http://joel.inpointform.net/software-development/pyramid-js-a-web-dependency-manager
- ControlJS – http://stevesouders.com/controljs
- StealJS – http://javascriptmvc.com/docs.html#!stealjs – JavascriptMVC
- LazyLoad – https://github.com/rgrove/lazyload
- NBL – http://berklee.github.com/nbl
- CurlJS – https://github.com/unscriptable/curl
- YepNopeJS – http://yepnopejs.com/
- PINF- https://github.com/pinf/loader-js
- Yabble – http://github.com/jbrantly/yabble
- ScriptJS – http://dustindiaz.com/scriptjs
Simple examples:
LoadRunnerJS:
1
2
3
4
5
6
7
8
9
|
// use some javascript files using('javascripts/jquery.js', 'javascripts/underscore.js', function() { $(function() { _(['foo', 'bar', 'baz']).each(function(i) { $(document.body).append('< p >' + i + '</ p >'); }); }) }); |
PyramidJS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
//set the rootPath. The rootPath determines //the base directory where all the files get loaded from. Pyramid.rootPath = './'; //Set up file dependencies Pyramid.newDependency({ name: 'standard', files: [ 'standardResources/jquery.1.6.1.min.js' ] }); Pyramid.newDependency({ name:'lookAndFeel', files: [ 'styles.css', 'customStyles.css', 'createAnimations.js' ] }); Pyramid.newDependency({ name:'main', files: [ 'createNamespace.js', 'models/person.js', 'init.js' ], dependencies: ['standard','lookAndFeel'] }); |
NBL :
src=”nbl.js” data-nbl=”[ [ ‘http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js’, ‘jquery.lightbox.min.js’, ‘jquery.carousel.min.js’ ], function(e){ jquery_loaded(e) }, ‘http://www.google-analytics.com/urchin.js’, function(){ urchin_loaded() } ]”>
InjectJS:
1
2
|
require.setModuleRoot("http://example.com/js/modules"); require.run("program"); |
CurlJS:
1
2
3
4
5
6
7
8
9
10
|
curl = { baseUrl: '/path/to/my/js', pluginPath: 'for/some/reason/plugins/r/here', paths: { curl: 'curl/src/curl', cssx: 'cssx/src/cssx', my: '../../my-lib/' }, apiName: 'someOtherName' }; |
YepNopeJS:<
1
2
3
4
|
yepnope({ test : Modernizr.geolocation, yep : 'normal.js', nope : ['polyfill.js', 'wrapper.js'] }); |
PINF:
1
|
program.json ~ { "boot": "github.com/pinf/loader-js/demos/HelloWorld/", "packages": { "github.com/pinf/loader-js/demos/HelloWorld/": { "locator": { "location": "./" } } } } package.json ~ { "uid": "http://github.com/pinf/loader-js/demos/HelloWorld/", "main": "main.js" } main.js ~ module.declare([], function(require, exports, module) { exports.main = function() { module.print("Hello World!\n"); } }); |
YabbleJS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
require.setModuleRoot(path) Use this to configure the root path to find modules. Think of this as a single entry in require.paths. require.useScriptTags() The default behavior is to use XHR+eval() and expects unwrapped modules. Call this method to configure Yabble to use script tags and expect wrapped modules. require.run(programId) This kicks off an application using the specified module ID. require.ensure(dependencies, callback) See Async/A for more details. Can be used to retrieve modules and run code without using require.run(). |
ScriptJS:
1
2
3
4
5
|
$script('analytics.js'); $script('library.js', function() { // do stuff with library. }); |