The code in the Hubbub project was organized in a very simple way using vanilla JavaScript without any special tools. This was done so that it was easy to get started with the project and understand how everything works. As you work on a real-world project, there are a variety of tools out there to help setup your project, organize your code, and build your project to be optimized for production. We'll walk you through a few of the important things to remember and some useful tools to help make it all manageable. Before we do that, though, let's look at fetching asynchronously and how to use Backbone.LayoutManager.
Up to now, all examples have been synchronous operations. This means we have not had to work around any I/O from network/file loading, timers, or anything else that may cause asynchronous behavior.
This is desirable when working without an HTTP server serving your markup. Since browsers forbid asynchronous requests unless your application is served from a web server, it's less than ideal to teach this method in examples.
The benefits of loading asynchronously through a method like AJAX is that you are able to break down templates into separate files with respective syntax highlighting, and thus you can easily pre-compile with existing build tools.
An example of this fetching implementation could look something like:
var MyView = Backbone.View.extend({
// Assign the file path to fetch the template contents from.
template: "templates/my-template.html",
// Since this method is now asynchronous, pass a callback to know
// when it's done.
fetchTemplate: function(done) {
// Use jQuery to asynchronously fetch the template.
$.get(this.template, function(contents) {
// Compile and send the template to the callback function.
done(_.template(contents));
}, "text");
}
});
// Create an instance to test fetch on.
var myView = new MyView();
// To access the template function now you would need to pass a
// callback to fetchTemplate.
myView.fetchTemplate(function(template) {
// Render the template.
template({ some: "data" });
});
This should be enough to help get you started with the idea of fetching asynchronously with Backbone.
We didn't cover this in Chapter 4: View, but let's take a quick look at an example of how a plugin View could look different from a typical Backbone.View:
var MyView = Backbone.Layout.extend({
template: "#someTemplate",
// Tell the View what data to provide to your template.
serialize: function() {
return {
model: this.model };
},
// Automatically have render defined for you, so just bind it to any event
// that should affect the View.
initialize: function() {
this.listenTo(this.model, "change", this.render); },
// Declaratively assign nested Views.
views: {
// Each line is a selector that exists within #someTemplate and a
// View instance that will be rendered and inserted into the element
// found by the selector.
"header": new HeaderView(),
"section": new ContentView(),
"footer": new FooterView() },
// Can attach jQuery plugins after rendering is complete.
afterRender: function() {
this.$el.someJqueryPlugin(); }
});
This is just enough to get your feet wet, but we felt it was important to show you a plugin View in Backbone. Now let's take a look at what is required with using Backbone on a production server.
While of course you want your application to generally behave the same way on your production server as it did from your local development server, there are some pretty significant differences in goals to keep in mind. Let's look at the main differences and discuss how modules and build tools can add harmony to an otherwise conflicting situation.
While you're writing code for your application, things should be setup in such a way that makes you, as a developer, the most efficient are:
When code is running on your production server; however, things should be setup in such a way that provides the best experience for end users, which means doing whatever you can to improve performance. Some examples are:
Now let's discuss a few tools to make all of this easier.
RequireJS is an excellent way to split your code into AMD modules (separate files), define dependencies between them, and then load your multiple JavaScript modules/files asynchronously without anything breaking.
In our Hubbub example, in index.html, you'll see we have a bunch of <script> tags. Browsers are forced to download and execute these files sequentially, which can be bad for performance. Using RequireJS (or another script loading tool) can allow multiple JavaScript files to be downloaded in parallel and then run in the correct order based on the defined dependencies needed for each module.
In the example below, we'll define two AMD modules and show how one can show a dependency on the other.
In models/issue.js:
define(['backbone'],
function(Backbone) {
var Issue = Backbone.Model.extend({
defaults: { name: 'Untitled' }
// remainder of Issue Model definition here...
});
}
return Issue;
});
In views/issue.js:
define(['backbone', 'models/issue'],
function(Backbone, Issue) {
var IssueView = Backbone.View.extend({
render: function() {
this.$el.html('Name:' + this.model.get('name'));
return this;
}
// remainder of Issue View definition here...
});
}
return IssueView;
});
Above we said that in development we wanted separate files for each logical section of code. RequireJS helps make this possible. But we also said that in production there should be as few files as possible. There's a tool, r.js, to help with this! If you've got Node installed on your computer, you can run this command before deploying your code to production:
$ node r.js -o baseUrl=. name=main out=main-built.js
If you want to learn more about RequireJS, r.js, and build optimizations, see RequireJS's Optimization page.
Grunt is a task-based command line build tool for JavaScript projects, that will help make easy many of the best practices described above.
Here's an example "gruntfile" to configure grunt with a Backbone project. With grunt installed and configured, you could simply run:
$ grunt release
and Grunt will, based on the gruntfile linked above:
By integrating a tool like grunt and having a "build" process, you can get the best of both worlds (easy to work with development environment, fast application in production). If you use grunt, be sure to check out grunt-contrib and all the plugins listed on gruntjs.com.
If you want to get started with using Backbone with RequireJS, we highly recommend checking out Backbone-Boilerplate. It nicely lays out:
An alternative to Grunt is Brunch, an "application assembler" that comes with many Backbone based "skeletons". Like Grunt, it automates linting, concatenating, precompiling and minifying. It is a flexible build tool and works with RequireJS or a simpler CommonJS module syntax. It's configuration files are less verbose than Grunt's, and it's easy to get started with. One of the advantages of using BackboneJS in your project is that there is a large community and many excellent tools to choose from.
Yeoman is a Google project that incorporates Grunt, but adds the following additional functionality:
If you prefer standalone tools over Grunt, some other great tools include UglifyJS, Closure Compiler, YUI Compressor, and the Asset Pipeline in Ruby on Rails.
All of the different tools out there can be overwhelming when beginning your Backbone development adventures (especially when you're already trying to learn a new language or framework), so don't feel like you have to use them all at once. Sometimes it's best to work on a small project and structure to get things done quickly and simply (like our Hubbub app). Once you're comfortable with Backbone, you'll see the value some of these tools can provide. And if you sweat the details of performance, it can make a big difference in your user's happiness!
There has been error in communication with Booktype server. Not sure right now where is the problem.
You should refresh this page.