Building Modern Web Applications with Backbone Boilerplate

Who Am I?

  • Senior Software Engineer at Bocoup
  • Backbone.js contributor
  • Backbone Boilerplate, LayoutManager, and use.js

  • Skydiver
  • Rock climber

@tbranyen

Code style

  • JavaScript not CoffeeScript
  • 2 space soft tabs
  • Strict 80 columns
  • Multiple var, semicolons, double quotes, and comma last

Context

Who is consuming?

  • Search engines (SEO)
  • Everyone (Accessibility)
  • Controlled environments (PhoneGap/Air)
  • Semi-controlled environments (Minimum browser requirements)

Examples

TweetDeck without JavaScript

Twitter without JavaScript

Grading development platforms

Evolution of my development process

Grade F

(Circa 2009)

Whats done right.

  • External file.
  • No globals except for namespace.
  • Used jQuery.

Whats done wrong.

  • One big file!
  • No templates (all DOM manip).
  • No module system.
  • No code structure library.
  • Completely unmaintainable!
  • No concatenation, minification, or linting.
  • All custom written besides jQuery.

Grade D

(Circa 2010)

Whats done right.

  • Code is broken out into separate files.
  • Relatively maintainable.
  • Leveraging plugins.

Whats done wrong.

  • No templates (all DOM manip).
  • No code structure library.
  • No module system.
  • No concatenation, minification, or linting.

Grade C

(Circa 2011)

Whats done right.

  • Code is broken out into separate files.
  • Shared namespace.
  • Custom code structure library.
  • Leveraging plugins and several libraries.
  • Mustache templates in separate files.
  • Full build process with: concatenation, minification, and linting.
  • Has a custom module system.

Whats done wrong.

If everything was done right, why a C?

I'm a tough critic

We can do better!

Code structure library

Backbone, Ember, Angular, etc.

Predictable filesystem

Your guide to organization and maintainability

Standard module definition

Code re-use and future-proofing

Robust build tools

Leverage existing tools to build the most efficient application

Grade B

(Circa 2012)

Whats done right.

  • Logical filesystem that is predictable.
  • Using AMD, a standard module definition pattern.
  • Building with tools that are reusable and far more efficient.
  • Utilizing open source libraries, not re-inventing the wheel.

Whats missing then?

  • Lots, we'll revisit this part.

Backbone.js limitations

  • No View management
  • No module support
  • No right/wrong way to structure an application

Backbone Boilerplate

A set of best practices and utilities for building Backbone.js applications.

https://github.com/tbranyen/backbone-boilerplate

Build tool is a separate project called grunt-bbb
[B]ackbone [B]oilerplate [B]uild.

Grunt

https://github.com/cowboy/grunt

  • Written by Ben Alman (Bocoup)
  • Task runner
  • Really good defaults
  • Super flexible (especially with plugins)
  • Gruntfile (grunt.js) serves as configuration for tasks
  • > 53 user contributed plugins

bbb

https://github.com/backbone-boilerplate/grunt-bbb

  • Complement to backbone-boilerplate, the scaffolding and build tool
  • Internally uses grunt-contrib and grunt-jasmine-task

What's included?

  • Backbone.js configured with sensible defaults
  • View management via LayoutManager
  • AMD via RequireJS configured to work out-of-the-box
  • Build system via Grunt & BBB
  • CSS via H5BP

Development philosophy

  • Never re-build during development
  • Almost never touch index.html
  • Server remaps paths to production assets
  • Build all source files into as few files as possible
  • Works without a server with minimal effort
  • Identical workflow in Windows/OS X/Linux

Workflow process

  1. Initialize a new project.
  2. Add new modules and templates.
  3. Develop using the built in server.
  4. Run the build tool.
  5. Deploy and map to production assets.

Getting started with BBB

# Get the Boilerplate without the build tool
git clone https://github.com/tbranyen/backbone-boilerplate
# Install bbb globally
[sudo] npm install bbb -g

# Create a new project
bbb init

Browsing the folder structure

timbranyen in ~/myapp λ tree
.
├── app
│   ├── app.js
│   ├── config.js
│   ├── main.js
│   ├── modules
│   ├── router.js
│   └── templates
├── assets
│   ├── css
│   └── js
│       ├── libs
│       └── plugins
├── favicon.ico
├── grunt.js
├── index.html
└── test

Module scaffolding

# Create a new module
bbb init:module

# Grunt prompt
Please answer the following:
[?] Module Name jetpack
[?] Do you need to make any changes to the above before continuing? (y/N) n

Writing app/modules/jetpack.js...OK

Initialized from template "module".

RequireJS (AMD)

http://requirejs.com

// Define someModule
define([/* optional dependencies */], function() {
  return { a: 1 };
});

// Consume someModule
require(["someModule"], function(someModule) {
  someModule.a === 1;
});

Inspecting the module

define([
  // Application.
  "app"
],

// Map dependencies from above array.
function(app) {

  // Create a new module.
  var Jetpack = app.module();

  /* ... */

  // Return the module for AMD compliance.
  return Jetpack;

});

LayoutManager

http://github.com/tbranyen/backbone.layoutmanager

Creating templates/layouts

# Create templates directory
mkdir -p app/templates/jetpack

# Create layouts directory
mkdir -p app/templates/layouts

# Create new layout
touch app/templates/layouts/main.html

# Create new template
touch app/templates/jetpack/header.html

Layout template

<header></header>

Template once its compiled

this['JST']['app/templates/jetpack/header.html'] = function(data) { return function (obj,_) {
var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('<h1>Page title</h1>\n');}return __p.join('');
}(data, _)};

Creating a View to consume the template

Jetpack.Views.Header = Backbone.View.extend({
  template: "jetpack/header"
});

useLayout function

Creates a new layout with the template name passed in. If its the same name, will reuse the layout

app.useLayout(name)

Assembling the layout in the Router

define([
  // Application.
  "app",

  // Jetpack
  "modules/jetpack"
],

function(app, Jetpack) {

  // Defining the application router, you can attach sub routers here.
  var Router = Backbone.Router.extend({
    routes: {
      "": "index"
    },

    index: function() {
      app.useLayout("main").setViews({
        "header": new Jetpack.Views.Header()
      }).render();
    },
  });

  return Router;

});

How does it know which template to use?

// Localize or create a new JavaScript Template object.
var JST = window.JST = window.JST || {};

/* ... */

fetch: function(path) {
  path = path + ".html";

  if (!JST[path]) {
    $.ajax({ url: "/" + path, async: false }).then(function(contents) {
      JST[path] = _.template(contents);
    });
  } 
  
  return JST[path];
}

How is CSS handled?

  • No pre-processor yet, unfortunately
  • index.css is the only stylesheet loaded
  • Contains @imports
  • Build process takes specific CSS and compiles them into new index.css

Release build

  • Lint source
  • Precompile all templates
  • Build all modules into a single file
  • Concatenate Almond - Templates - Source
  • Minify source and CSS.

Almond

https://github.com/jrburke/almond

  • Very lightweight shim for RequireJS
  • Only works with compiled assets (no lazy load)

The command

bbb release

Build output

Running "min:dist/release/require.js" (min) task
File "dist/release/require.js" created.
Uncompressed size: 449263 bytes.
Compressed size: 47895 bytes gzipped (137780 bytes minified).

Running "mincss:dist/release/index.css" (mincss) task
File "dist/release/index.css" created.
Uncompressed size: 9657 bytes.
Compressed size: 1439 bytes gzipped (3675 bytes minified).

Running the integrated server

# Development
bbb server

# Production assets
bbb server:release

Example nginx configuration

server {
  set $appdir /path/to/app;  

  location / {
    root $appdir;
    try_files $uri /index.html;
  }

  location /app {
    alias $appdir/dist/release/;
  }

  location /assets/js/libs {
    alias $appdir/dist/release/;
  }

  location /assets/css {
    alias $appdir/dist/release/;
  }
}

Others

  • brunch.io (Custom)
  • yeoman.io (Grunt) - not released
  • Not tied to any specific code structure library
  • Both are good, but not Grade A

Grade A

(El futurino)

  • CSS pre-processor integration (no build step to test)
  • Image optimizations
  • Deployment process
  • Dynamic builds
  • Fully integrated modules
  • Source maps
  • Seamless package manager integration
  • Internationalization
  • Development REPL

Questions!