November 11, 2014 · requirejs javascript

Require.js Dependency management - part1

Follow me on Twitter

Jump to complete source code source code.

Visit Keleraba (Backbone, Require, Handlebars) boilerplate project on github. Caution Keleraba is in early phase of development and serves as a learning resource.

This is introductory RequireJS tutorial in RequireJs series(part1, part2). Reading material about AMD and RequireJS is in references section of article.

Table of contents:

  1. Introduction
  2. Basic structure
  3. Prototypal inheritance and Require.js

Introduction

Before or after reading this article I strongly recommend to read documentation on RequireJS website. James Burke did great job here.

RequireJS is a Javascript file and module loader which allows for asynchronous JavaScript loading and dependency management(can be used to load more than just Javascript files).

Require.js is a dependency management and asynchronous script loading tool(AMD library). What that means? AMD stands for Asynchronous Module Definition.

RequireJS is asynchronous which means that you can do non-blocking and parallel fetch of your javascript files.

RequireJS is built around Module pattern. Modern web applications tend to have fairly complex front-ends. Module pattern should improve maintability of our bloated javascript code. Remmember those giant javascript files and svn merge conflict hell on your last project? Javascript code should consist of smaller components enforcing separation of concerns and avoiding globals since modules are wrapped by closures.

RequireJS is well defined and standardized. While we wait ES-Harmony to knock on our doors, libraries like RequireJS are giving us hint how we should structure our applications. Require.js works in current browsers.

Basic structure

STEP 1 - How to structure RequireJS project

This is our sample RequireJS project structure.

 |-[wepapp]
 |--- [build]
 |----- r.js
 |----- build.js
 |----- build.single.js
 |--- [js]
 |------ [app]
 |------ [lib]
 |--------- jquery.js
 |----- app.js
 |----- app-built.js*
 |-index.html
 |-readme.md

Build scripts and RequireJS optimization tool r.js are inside [build] directory.

Application code reside in [webapp/js/app] directory. This is place where you should place all of your /Model/View/Router/Template code.

Libraries like jQuery, Backbone, Handlebars and others are inside [webapp/js/lib] directory.

Application config and main entry point is [webapp/js/app.js]

STEP 2 - How to include RequireJS

Your [webapp/index.html] should look like this

 <html>
 <head></head>
 <body>
 <div id="test">Some sample content</div>
 <script data-main="js/app.js" src="js/lib/require.js"></script>
 </body>
 </html>

Attribute [data-main="js/app.js"] is entry point of our application. This means that RequireJS will first load [js/app.js] after initialization.

STEP 3 - How to configure RequireJS

Open your [webapp/js/app.js] file.

 requirejs.config({
     baseUrl: 'js/lib',
     paths: {
         app: '../app',
         jquery: 'jquery'
     }

 });

 requirejs(['jquery'], function($) {
     console.log($('#test'));
 });

[baseUrl: 'js/lib'] - RequireJS loads code realative to directory specified in baseUrl. By default baseUrl is set to the same directory as data-main. If you dont specify data-main attribute and baseUrl property is not present in RequireJS config, than default path is directory that contains html page which include RequireJS library. base url

Attribute [paths] paths config

STEP 4 - How to use requre() and define() in RequireJS

require() and define() are most important concepts in RequireJS.

define() - Used for module definition. Consists of module wrapper, dependency list and definition function.

define(['dependency1', ['dependency2', ['dependency3'], function(dependency1, dependency2, dependency3) {  
     var Module = function(value) {
         this.property = value;
     }
     return (Module);
 });

require() - Used for dependency loading Consists of public api, dependency list, callback function

 requirejs(['dependency1', ['dependency2', ['dependency3'], function(dependency1, dependency2, dependency3) {
     // You can use your imported module here
     var instance = dependency1;
     var constructorFunction = new dependency2();
 });

Prototypal inheritance and Require.js

Skip to complete source code.

With RequireJS you can organize your modules in separate files with ease.

Now we are going to add 3 object definitions to our project: Category, Item and Specialized Item.

 |-[wepapp]
 |--- [build]
 |----- r.js
 |----- build.js
 |----- build.single.js
 |--- [js]
 |------ [app]
 |--------- [category]
 |------------ category.js
 |------------ item.js
 |------------ specializedItem.js
 |------ [lib]
 |--------- jquery.js
 |----- app.js
 |----- app-built.js*
 |-index.html
 |-readme.md

With RequireJS you can provide function or object in return statement. We are going to use this feature to retrieve constructor functions for our object.

With RequireJS you can import and use external dependencies inside your module. We are going to use this feature to achieve prototypal inheritance in RequireJS.

Category is collection of our Items. [webapp/js/app/category/category.js]

 "use strict";

 define(function() {

     var Category = function() {
         this.name = 'Default name';
         this.category = 'Default category';
         this.items = [];        
     }

     Category.prototype.showItems = function() {
         for (var i = 0, len = this.items.length; i < len; i++) {
             console.log(this.items[i]);
         }
     }

     Category.prototype.addItem = function(item) {
         this.items.push(item);
     }

     return (Category);

 });

Item is base object. [webapp/js/app/category/item.js]

 "use strict";

 define(function() {

     var Item = function(itemName) {
         this.name = itemName;
     }

     Item.prototype.getItemName = function() {
         return this.name;
     }

     return (Item);

 });

[webapp/js/app/category/specialItem.js]

SpecialItem is object that extends Item. In order to extend Item object, we must import Item as a dependency['./item']. Now Item object is available for use in our new SpecialItem module.

 "use strict";

 define(['./item'], function(Item) {

     var SpecialItem = function(itemName) {
         this.color = 'Default color';
         this.weigth = 'Default weigth';
         Item.call(this, itemName);
     }

     SpecialItem.prototype = new Item;
     SpecialItem.prototype.constructor = SpecialItem;

     return (SpecialItem);

 });

Source code listing

[webapp/js/app.js]

 requirejs.config({
     baseUrl: 'js/lib',
     paths: {
         app: '../app',
         jquery: 'jquery'
     }
 });

 requirejs(['jquery', 'app/category/category', 'app/category/item', 'app/category/specialItem'],
 function($, Category, Item, SpecialItem) {
     var c1 = new Category();
     c1.items.push(new Item('RequireJS in Action'));
     c1.items.push(new Item('Javascript Good parts'));
     c1.items.push(new Item('Some book'));
     c1.items.push(new SpecialItem('Special promotion book'));
     c1.showItems();
     console.log(c1);
 });

[webapp/js/app/category/category.js]

 "use strict";

 define(function() {

     var Category = function() {
         this.name = 'Default name';
         this.category = 'Default category';
         this.items = [];        
     }

     Category.prototype.showItems = function() {
         for (var i = 0, len = this.items.length; i < len; i++) {
             console.log(this.items[i]);
         }
     }

     Category.prototype.addItem = function(item) {
         this.items.push(item);
     }

     return (Category);

 });

[webapp/js/app/category/item.js]

 "use strict";

 define(function() {

     var Item = function(itemName) {
         this.name = itemName;
     }

     Item.prototype.getItemName = function() {
         return this.name;
     }

     return (Item);

 });

[webapp/js/app/category/specialItem.js]

 "use strict";

 define(['./item'], function(Item) {

     var SpecialItem = function(itemName) {
         this.color = 'Default color';
         this.weigth = 'Default weigth';
         Item.call(this, itemName);
     }

     SpecialItem.prototype = new Item;
     SpecialItem.prototype.constructor = SpecialItem;

     return (SpecialItem);

 });

In next part of Require.js series I will talk about r.js RequireJS optimization and build tool.

Follow me on Twitter

References

  1. http://addyosmani.com/writing-modular-js/
  2. http://msdn.microsoft.com/en-us/magazine/hh227261.aspx
  3. http://tomdale.net/2012/01/amd-is-not-the-answer/
  4. http://tagneto.blogspot.com/2012/01/reply-to-tom-on-amd.html
  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket
Comments powered by Disqus