Setting up a BackboneJS application with RequireJS

Scouring the internet I was unable to find particularly good instructions on how to structure a backbone js application for use with requirejs. A lot of tutorials one might find on the internet often teach new comers to put everything into one large app.js javascript file.

// app.js
var AppModel = Backbone.Model.extend({
 // ...
})

var AppCollection = Backbone.Collection.extend({
  model: AppModel,

  // ...
})

var AppView = BackBone.View.extend({
  // ...
})

var App = Backbone.Router.extend({
  routes: {
    '/': 'index'
  },

  index: function() {
    new AppView();
  }
})

new App();
Backbone.history.start();

Then inject that into the header of the html page.

<html>
  <head>
    <title>My App</title>
    <!-- various app dependencies like jquery, backbone, underscore etc -->
    <script type="text/javascript" src='app.js'></script>
  </head>
  <body>
    <div id="content">
      <!-- all your wonderful html in here -->
    </div>
  </body>
</html>

Then, once the application starts to grow, the next strategy is to use multiple <script> tags to include each of the relevant application dependencies. I have seen far too many instances where the content of the <head> tag grows out of control and is an absolute mess.

The use of AMD is a very effective pattern for overcoming the issues associated with multiple package dependencies, asynchronous loading, as well as effective application structure. RequireJS does a much better job of explaining why this is such a desirable pattern.

Since Backbone is such a relatively light framework, it is a reasonable candidate for an explanation of application structure and usage of requirejs.

Consider the following basic application structure:

base_project_dir/js/
+- app
   +- collections
      -- model_collection.js
   +- models
      -- model.js
   +- templates
      -- main_view.tmpl
   +- views
      -- main_view.js
   -- app.js
   -- config.js
   -- router.js
+- lib
   +- backbone
      -- backbone.js
   +- jquery
      -- jquery.js
   +- underscore
      -- underscore.js
-- require.js
-- text.js
-- main.js

Implementing this structure is relatively straightforward. The main application models, views, collections and router contain the following structure:

// /js/app/collections/model_collection.js
define(['app/config', 'app/models/model'], function (config, AppModel) {
  return Backbone.Collection.extend({
    model: AppModel,
    url:   config.baseName + '/models',

    // ...
  });
});

// /js/app/models/model.js
define(['app/config'], function (config) {
  return Backbone.Model.extend({
    // ...
  });
});

// /js/app/views/main_view.js
define(['app/models/config', 'text!app/templates/main_view.tmpl'], function (config, tmpl) {
  return Backbone.View.extend({
    template: _.template(tmpl),

    // ...
  });
});

// /js/app/router.js
define(['app/views/app_view'], function (AppView) {
  var AppRouter = Backbone.Router.extend({
    routes: {
      '/': 'index'
    },

    index: function () {
      var view = new AppView();
      view.render();
    }
  });

  return {
    initialize: function () {
      new AppRouter();
      Backbone.history.start();
    }
  };
});

We can't forget the application view template:

<!-- /js/app/templates/main_view.tmpl -->
<div class='template-class'>
  <!-- other markup... -->
</div>

We keep a config module to store any relevant configuration required at runtime. It might also make sense to define a utilities module, but its not essential for an application of this size. If it were, it could easily be added inside the existing application structure.

// /js/app/config.js
define(function () {
  return {
    key1: 'value1',
    key2: 'value2',
    key3: function () {};

    // ...
  };
});

With the main application files defined, we can then go about starting our application using requirejs:

// /js/app/app.js
define(['app/router'], function (AppRouter) {
  return {
    initialize: AppRouter.initialize
  };
});

// /js/main.js
requirejs.config({
  paths: {
    // Libraries
    jquery: './lib/jquery/jquery',
    underscore: './lib/underscore/underscore',
    backbone: './lib/backbone/backbone'
  },

  shim: {
    // Define any vendor packages that require dependencies to be loaded
    'backbone': {
      deps: ['underscore', 'jquery'],
      exports: 'Backbone'
    }
  }
});

define(['jquery', 'underscore', 'backbone'], function ($, _, Backbone) {
  require(['app/app'], function (App) {
    // Lets get this party started!
    window.App = App.initialize();
  });
});

This now means our initial markup is a lot cleaner:

<html>
  <head>
    <title>My App</title>
    <!-- No more dependencies -->
    <script type="text/javascript" src='require.js' data-main='js/main'></script>
  </head>
  <body>
    <div id="content">
      <!-- all your wonderful html in here -->
    </div>
  </body>
</html>

The data-main attribute in the <script> tag is part of the requirejs api and points to the entry point of our application. The requirejs configuration allows us to define our 'global' dependencies once within the application, and have it available within the namespace from that point on.

RequireJS also comes with a packager, which allows you to package all of your application js and dependencies into one file that gets parsed by the browser. All that needs to change is that the data-main attribute should point to the location of the built application file.