RequireJS

Creating a dynamic modular multi-page app with Backbone.js and RequireJs

So, as part of my To-Learn list for 2013 I’m currently messing around with Javascript tools and libraries.

It has been a long way since we used Javascript mostly to validate forms and show alert boxes.

Today we have so many good tools that allows us to write better front-end applications, that’s hard to keep up.

Recently I’ve been playing with Backbone.js, an awesome library that helps you separate content/logic in your front-end application. And while I’m no expert on it, one of the first things I noticed is that Backbone doesn’t explain how you should lay out your code.


This can be good, but it can also lead to architecture design disasters if not used correctly. Specially if you are developing multi-pages website.

So, in this tutorial I’m going to show you how to create a good foundation for your application using AMD design pattern with Backbone.js and RequireJs.

Notice: This is not a tutorial for beginners on BackboneJs or RequireJs. It's expected that you have some experiencie with these tools.

TL;DR

I show you how to create a basic boilerplate multi-page using Backbone.js and RequireJs.
I’ve placed a improved version of this tutorial on github.

Intro

I’m not going to explain in full detail what each of these libraries do, I assume most of you already know it, if not, just click on the likes above.

We’re going to create a sort of boilerplate, that can be reused in future projects, so in order to do this we will need a few extra things to keep everything tidy.

Bower Setup

The first thing you want to do, is to install Bower. This is a very light and solid package manager for the web. You can install/update/remove external vendor packages from your command line.

From your command line, create a folder somewhere in your web root folder.

$ mkdir myapp && cd myapp/

At the root of myapp/ create a new file called “.bowerrc” and add this

{
  "directory": "src/scripts/vendors"
}

This will make bower install it’s dependencies under the “directory” param.
Save the file and go back to the command line.

Create another file called “component.json” and past this.

{
	"name": "MyApp",
	"version" : "1.0.0",
	"dependencies": {
		"jquery": null,
		"backbone-amd": null,
		"underscore-amd": null,
		"requirejs": null,
	}
}

This file will allow Bower to install all of this dependencies for us.

Let the magic begin. From the command line type:

$ bower install

You should start see Bower fetching all those libraries from git and copying them into “src/scripts/vendors”.
By default, Bower copies the entire repository of each library. So it’s up to you if you want to delete some extra files you don’t want. You can also just specify a single file, but read the Bower documentation for more info about this.

Defining a directory structure

Now, let’s create our bundle root folder.

$ mkdir src/scripts/bundles

This folder will be the home of our bundles.

$ mkdir build/

This folder will contain our building scripts, that will merge and minify our dirs into an output dir.

Loading…

Now, let’s get our hands dirty.
Open up your favourite code editor, and create a file “src/index.html”

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name='viewport' content='width=device-width'/>
    <title>Backbone Boilerplate</title>
    <script data-main="scripts/main" 
        data-root="/myapp/src/"
        src="scripts/vendors/requirejs/require.js"></script>
</head>
<body>

    <h1>My title</title>

</body>
</html>

Notice that data-root is a custom field we add. This field defines the default web url root path. If your root path is “/” you can remove this.

data-main is the main entry point for RequireJs, this will load the file “src/scripts/main.js”, notice that we didn’t need to add “.js” extension. RequireJs does this for us.

So let’s create this file.

(function () {

    'use strict';

    requirejs.config({
        baseUrl: "scripts/",
        paths: {
            "jquery": "vendors/jquery/jquery",
            "underscore": "vendors/underscore-amd/underscore",
            "backbone": "vendors/backbone-amd/backbone",
        }
    });

    require(['app'], function (app) {
        app.initialize();
    });

}());

This file will be responsible for creating a config structure that will be used by RequireJs.

On line 17, RequireJs will need to include a “app.js” file, so let’s create it.

define([
    'jquery',
    'underscore',
    'backbone',
    'router'
], function ($, _, Backbone, router) {

    'use strict';

    // Add your modules routing here
    router.route("/*", "home", function () {
        this.loadModule("bundles/todos/main");
    });

    var root = $("[data-main][data-root]").data("root");
    root = root ? root : '/';

    return {
        initialize: function () {
            Backbone.history.start({
                pushState: true,
                root: root
            });
        }
    };
});

This file will allows to create our routing patterns for our bundles.
Inside the define() function, you’ll notice that it will include our dependencies and a new file, called “router”, you haven’t created this yet.
It will also start the Backbone History. Also, notice that it’s using our custom data-field “data-root” around line 15.

To complete our basic dynamic loading, we just need to create one last file. The router.js.

define(["underscore", "backbone"], function (_, Backbone) {

    'use strict';

     // Router
    var Router = Backbone.Router.extend({
        loadModule: function (module) {
            require([module], function (module) {
                module();
            });
        }
    });

    return new Router();
});

This file will create a new Object from Backbone.Router and add a new function called loadModule(). This function will load our Bundles dynamically. If you look at the file “app.js” you will see the following code.

router.route("/*", "home", function () {
    this.loadModule("bundles/demo/main");
});

This must be added by you. What this means is that everytime a url matches “/*” this will trigger an event that will load the “bundles/todos/main.js” file. We haven’t created this yet.

And we are done with the basic foundation for our multi-page app.
To wrap it up, here’s the file tree.

.
├── .bowerrc
├── component.json
└── src
    ├── index.html
    └── scripts
        ├── app.js
        ├── bundles
        ├── main.js
        ├── router.js
        └── vendors

Creating our bundle

The idea behind a bundle is to isolate specific sections of our websites in it.
For example.

http://www.myhost.com/ —> /src/scripts/bundles/home
http://www.myhost.com/users —> /src/scripts/bundles/users
http://www.myhost.com/invoices —> /src/scripts/bundles/invoices

And since we are using requireJs, we can easily create dependencies and share resources between bundles.

So let’s create a basic bundle.
Inside “myapp/src/scripts/bundles” create a directory named “demo” with the following structure

.
├── collections     --> Backbone Collections
├── models          --> Backbone Models
├── templates       --> Mustache Templates
├── tests           --> To store unit tests
├── views           --> Backbone Views
└── main.js         --> Bundle entry point

The main.js required by all bundlers. It will be used as the entry point when a bundle is loaded.

Here’s an example

define([ "./views/main"], function (MainView) {

    'use strict';

    return function () {

        var mainView = new MainView();
    };

});

This file will load a Backbone view object from “views/main.js” file.

Building

“Building” means that we will merge and minify/uglify our source code inside “myapp/src/” and output into a new folder “myapp/dist”.

We will endup with a single merged file for each bundle, and a single common file for our global dependencies like Backbone.js, RequireJs, jQuery and so on.

For eg. All of our files inside “myapp/src/scripts/bundles/demo” will be merged into one “main.js” file in the output directory.

As you know, this should be used on production only.

The major difference here, is that since we are using this for multi-page websites, we really don’t want to merge every thing into one big file, or having all bundles include the common libraries.

So by using the scripts below, we will organize our output code into single files that are loaded on demand.

When we created our directory structure, we created a dir at “myapp/build”.
Inside that directory create two new files

({
	appDir: '../src/',
	baseUrl: './scripts/',
	dir: '../dist',
	mainConfigFile: '../src/scripts/main.js',
	optimizeCss: 'none',
	paths: {
		requireLib: '../scripts/vendors/requirejs/require'
	},
	modules: [
		// WARNING: Do not remove this entry, it will be required
		// by all your bundles.
        {
            name: 'main',
            include: ['requireLib', 'main', 'app'],
        },


        //
        // Add your bundles here.
        // Make sure you always exclude 'main', 
        // unless you want to have
        // one single big file with every bundle 
        // and dependency. This is
        // not recommended.
        //
        {
            name: 'bundles/demo/main',
            exclude: ['main']
        }
    ]
})

In a nutshell, what we are doing here is specifying which bundles should be included, and make sure none of the bundles are merged with the “main” common scripts. Resulting in lighter files for the bundles.

r.js -o app.build.js skipDirOptimize=true
rm -rf ../dist/scripts/vendors
rm ../dist/scripts/router.js
rm ../dist/scripts/app.js
rm ../dist/build.txt

This is the build bash script. It will run r.js and output the result into “myapp/dist” and remove some unused folders and files.

You need to install r.js first.

$ cd build/ 
$ chmod +x build.sh
$ ./build.sh

You should start seeing a bunch of files being merged, and minified.
After it’s completed, you should see a new folder “myapp/dist”. It should contain all of your code, but without the vendors directory and other files like “router.js”, “app.js”.

Instead you should see only a “main.js” file. If you open this file it should include all the common libraries, including require.js

So in your new “myapp/dist/index.html” file you can change the script to

    <script data-main="scripts/main" 
        data-root="/backbone-boilerplate/src/"
        src="scripts/main.js"></script>

because “scripts/vendors/require/require.js” no longer exists.

Conclusion

And there you have it.
A basic boilerplate form multi-page websites using Backbone and Requirejs.

I’ve created a more completed boilerpate called Backbone-Boilerplate (creative name right?) that includes the concepts we are described here and goes a little further by using Unit testing, Template engine and including a Todo demo bundle for your to check it out.

I highly recommend your check this repository out, as it includes a more detail information on how to create bundles.

I also hope you’ve learned something, and if I missed or you have other suggestions please let me know in the comments below.

13 thoughts on “Creating a dynamic modular multi-page app with Backbone.js and RequireJs

  1. Permalink  ⋅ Reply

    Ron Reiter

    January 28, 2013 at 1:47pm

    Not a good idea to link module loading and routing. The whole app should be loaded, and the routing should only switch between states of the application. The router should depend on the application view, and should not initialize before the application is loaded.

    • Permalink  ⋅ Reply

      Henrique B.

      January 28, 2013 at 2:08pm

      You are right if you were to use it in a single-page app. This however is to make it work for multi-pages apps. I have done this way in order to avoid having this big compiled JS file with all the merged modules in it.

      By using this, you only load the code needed for that particular page.

      • Permalink  ⋅ Reply

        zsitro

        March 29, 2013 at 9:45am

        Agree! I was looking for this “on demand loading” solution for days..
        I think this is the definite way to load subpages in your Backbone application without polluting the memory with unnecessary assets.

  2. Permalink  ⋅ Reply

    Philip Thrasher

    February 22, 2013 at 1:19pm

    Great article. Ron, you’re mostly correct. However, what if I’m building a larger web application wherein I have different “areas” that are completely different functionality. For instance, what if I’m developing google docs. I’d need code minified for spreadsheet, but separate code minified for the presentation designer app. They may have some shared code, but there would also be a ton of code you wouldn’t want to load in both instances.

  3. Permalink  ⋅ Reply

    Charlie

    April 15, 2013 at 9:19pm

    Great article and very enlightening, Henrique.

    I’m trying to understand what you meant to say with “module()” in the “loadModule” function of “router” module. did you mean: return module. I recently started to work with AMD so I apologize if this seems too obvious but I just can’t see it. thanks.

    • Permalink  ⋅ Reply

      Walter Macambira

      April 17, 2013 at 11:51pm

      Look this: require([module], function (module) { module(); });

      The first module is the string “bundles/home/main”, the second is a parameter of the callback function, it will be what is returned by “bundles/home/main” (because RequireJS is designed to behave this way). In this case, it is a function(), because the main.js file returns a function (look at it). So, the third module, is not the string “bundles/home/main”, it is actually the function() returned. As it is “module()”, it is actually just executing the function returned.

      Hope I helped.

  4. Permalink  ⋅ Reply

    Walter Macambira

    April 17, 2013 at 9:20pm

    I am not an expert on this subject, but I will suggest a way to avoid memory leaks due to views event bindings and other bindings to DOM elements. To achieve this, you need to add in your loadModule function a view transition mechanism that would be responsible for calling a “clear” method from your current view. For this, your main.js (for each bundle), instead of returning a function with the MainView instantiation, would return the view itself. So your router would be aware of which MainView-bundle is loaded and be responsible for calling its “clearing” method to avoid memory leaks. This way, loadModule would also be responsible for adding the view’s el to your app container element. Hope I helped!

    By the way, greate article, helped me a lot understanding a better approach for a multi-page app with Backbone.

    • Permalink  ⋅ Reply

      Charlie

      April 21, 2013 at 8:04pm

      Definitely helpful, Walter!

      I took another look at RequireJS docs, too. I’m used to returning object from modules.

  5. Permalink  ⋅ Reply

    Jon West

    April 25, 2013 at 5:06pm

    @ Henrique Great article! I’m currently struggling with Backbone’s lack of organization in a large single page app which really has multiple large views/pages/sections. What you’ve written makes me feel like I’m not crazy for feeling like the app isn’t quite the same as the single view todo apps I’ve seen every where. Do you have any examples where you switch between pages? I noticed that you only have 1 bundle defined so the app really only has 1 page. Managing the transitions between various pages is tricky at best and messy and awkward at worst.

    • Permalink  ⋅ Reply

      Henrique B.

      April 26, 2013 at 8:44am

      Hi Jon, what I meant was that each page would be a bundle. So say /(index) would be a “home” bundle, /users would be a “users” bundle and so on.

      Eg:

      router.route(“/users”, “users”, function () {
      this.loadModule(“bundles/users/main”);
      });

      Hope this helps

  6. Permalink  ⋅ Reply

    alessio

    May 16, 2013 at 12:47am

    Articolo interessante e colgo l’occasione per complimentarmi per questo sito! veramente ben fatto e con tanti articoli utili!

Leave a Reply

Your email will not be published. Name and Email fields are required.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>