Backbone.js is a great client-side MV* (not MVC) JavaScript framework that provides structure to JavaScript applications by providing View, Model, Collection, and Router classes. Backbone also provides a pub sub (publish subscribe) mechanism, by allowing each of it’s objects to trigger and bind to events.
Backbone Views act as a combination of traditional MVC Views and Controllers, since they allow you to organize all of your JavaScript event handlers while also providing a mechanism for adding dynamic HTML to your page through the optional use of JavaScript templates. Views will also often be where you set data on your Models.
Here is an example Backbone View class:
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 31 32 33 34 |
|
Backbone Models store all of your applications business logic and data. This allows you to organize all of your application’s data validation inside of your Models. In most cases, Models should not know about any of your Views, or the DOM. There are certain design patterns (eg. modelbinding) where Models are aware of the DOM and set data on it, but most patterns do not implement this strategy since it is beneficial for Model’s to be completely decoupled from other pieces of the application.
Here is an example Backbone Model class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
If your application is complex, it is also helpful to use Backbone Collections, which provides a mechanism to interact with many different Model instances. Since Backbone has a hard dependency on Underscore.js, you are able to utilize many Underscore.js methods to interact with Collections. Keep in mind that you may alternatively use lodash, a drop-in replacement library for Underscore.js that provides cross browser bug fixes and performance improvements for Underscore.js. Lodash also allows custom builds, so you can only use a minimum number of Underscore.js functions if you like (reducing file size).
Here is an example Backbone Collection class:
1 2 3 4 5 6 |
|
Backbone Routers are typically used to define “routes” in your application. Routes are essentially a unique page in a traditional web application, but in a one page JavaScript application, they help organize your client-side logic without requiring a page refresh (traditionally appending a route as a hash tag to a browser’s url or using the new HTML5 push state api to silently track page state).
Here is an example Backbone Router class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Backbone Events are an important concept in Backbone, since they provide you with an easy mechanism to use the pub sub pattern and decouple your code. Backbone triggers certain events by default (eg. a Model change event is triggered after a Model property has been validated and saved), but Backbone also allows you to trigger and bind to custom events. This is an extremely useful pattern, since it allows many different classes to listen to one class, without that one class knowing about any of its listeners.
Here is an example Event being triggered by a Backbone Model class:
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 |
|
Here is an example Event being bound by a Backbone View class:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
Require.js serves two different purposes than Backbone.js. Require.js is an AMD (Asynchronous Module Definition) script loader that asynchronously loads your JavaScript to improve page load performance, while also providing you the ability to organize your JavaScript into self contained modules. Each JavaScript file represents a module.
Each module is enclosed in a define tag that lists the module’s file dependencies, and keeps the global namespace free (essentially acting as a JavaScript closure). Since none of your modules are global, inside of each module, you need to declare which other modules are dependencies and pass them to your current module. This provides a solution for limiting global variables and dependency management. This solution is much better then having many script tags in a single page, which can be cumbersome to keep track of which files depend on which other files. It also encourages you to decouple your JavaScript logic (instead of the traditionally hard to read one page JavaScript application).
Here is a more advanced example of a Backbone View class used with Require.js:
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 31 32 33 34 35 36 37 38 39 40 41 42 |
|
Backbone.js and Require.js perfectly complement each other, but there is one problem… Backbone.js is not AMD compatible. To be AMD compatible, a script must declare itself as a module by calling the define() method if it exists and list it’s dependencies. Backbone decided to not natively support AMD/Require.js, because it’s creater, Jeremy Ashkenas, felt it was unnatural to have to change Backbone’s source code so that it would work in Require.js and other AMD loaders. Luckily for us, Backbone contributor, Tim Branyen, created Use.js for this exact reason.
Looking at the previous code example, you will notice that I am using the Use.js plugin to include Backbone as a dependency to my View class. Use.js allows you to use (no pun intended) scripts that are not AMD compatible, by setting a one time configuration (typically in a main.js file or initialization file), and then prefixing dependencies with use!
.
Here is a typical Use.js configuration with Backbone.js and Underscore.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Note: Require.js 2.0 added support for loading non-AMD compatible scripts by using the Shim configuration. You can find a more detailed explanation of the Shim configuration in my next blog post. If you want more in-depth explanations and examples of how to setup Backbone.js, Require.js, and the Shim configuration, check out the Backbone-Require-Boilerplate I recently created on Github.
An additional benefit of using Require.js is you can optimize (concat, minifiy) your entire project or single JavaScript files (depending on your preference) using r.js, a library written by James Burke that allows you to use the Require.js Optimizer. All you need to do is install either node.js or rhino (both can be used as command line tools but node.js is recommended) and create a build file.
Here is an example of an app.build.js configuration file:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
Obviously your app.build.js file will most likely be different, but hopefully this helps get you on the right track (note: I took this directly from my Backbone-Require-Boilerplate project).
Considering the many advantages of using Backbone.js and Require.js together, I hope I have convinced you to try out these great open source projects. Happy coding!