Skeleton for a Backbone, Marionette application using Require.js, Bower, Grunt and LESS

I just managed to successfully change my first flat tire. Feels great. I won’t feel that good tomorrow, when I have to pay for a new one. So let’s use the current hype to learn how to set up this skeleton for a Backbone, Marionette application using Require.js, Bower, Grunt and LESS. In the readme you can learn how to start using it right away.

Setting up the repository

We will start by creating a github repository for our new project. I am assuming you have git installed. Browsing through the commit history of the repository in github might be a good idea to follow this post.

Once you have created your new empty github repository, clone it into your local folder, in my case:

 git clone https://github.com/mezod/skeleton.git

Now that we have our new empty repository in our local it’s a good moment to define what do we want git to ignore when pushing our commits. To do so, create a file in the root of the project called “.gitignore” with the following content:

app/scripts/vendor  
node_modules  
dist  

It is too early to explain why we need to ignore changes on those folders, but basically:

  • In app/scripts/vendor we will have all those js files for vendors and libraries our project depends on, such as jquery or backbone.
  • In node_modules we will have all those js files for tools we will use during the development of our project, such as grunt.
  • In dist we will have all those files specifically generated for the production environment, such as the minified versions of our .css and .js files.

Initial directory structure

Let’s now define the basic directory structure for our project. First we need to create the “app” folder in our project root. This is where our entire js app is going to be. Within this directory we create the following folders: “fonts”, “images”, “scripts”, “styles” and “templates”. For now, let’s also create a “vendors” folder inside “scripts” for our vendor scripts. At this point, our directory structure should look like:

/skeleton
    /app
        /fonts
        /images
        /scripts
            /vendor
        /styles
        /templates

Finally, we can already create our index.html inside our app folder. Something like:

<!DOCTYPE html>  
<html>
  <head>
    <title>skeleton</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="description" content="A simple kanban for multiple personal projects.">
    <meta name="viewport" content="width=device-width">
  </head>
  <body>
  </body>
</html>

Installing bower

bower Our next step is to install Bower. Bower is a package manager especially thought for the front-end and we will be using it to download and manage all the dependencies for our skeleton, such as for example, jquery, backbone or marionette. In other words, we won’t be needing to go and find all the necessary .js files for our app to work, Bower will do it for us.

The best way to install Bower though, is using another package manager, npm. This can be quite confusing at first but this StackOverflow answer explains us the purpose of each package manager. To synthesize, npm is designed to manage node.js modules in general, working also for front-end packages. It takes care of the dependencies between packages using nested dependency tree, which is really great on the server side where you don’t have to care much about space and latency. As you don’t have to care about dependency conflicts you might end up with three copies of jQuery. However, this is not so great on the client side. Here is where Bower might appear more suitable. Bower is conceived uniquely for the front-end, thus it only manages front-end related packages, and its flat dependency tree puts the burden of dependency resolution between packages on the user. That’s why we will be using RequireJS.

In conclusion, most of the projects normally use both package managers, and we will be no different. We will use Bower for the front-end packages and npm for developer tools like Grunt.

So after this theory block (hey! Wake up!), let’s rewind. We want to install Bower using npm, so we need to install npm and node.js first.

I am currently on a Windows, so I basically had to go to Nodejs.org and get the binary. If you are on a Mac you can get the .pkg here. For Ubuntu/Debian users:

sudo apt-get install nodejs npm

Independently of how you installed them, make sure it worked with:

node  -v
npm -v

With npm installed, installing bower is very easy:

npm install –g bower

The “-g” flag means we are installing it globally. This is useful so that you can run Bower on any other project folder in your system.

To make sure bower is successfully installed you can

bower -h

which should provide you with a list of commands and its descriptions.

Using bower

Once bower is installed, you can do things like:

bower install jquery

which will automatically download jquery and put it into a /bower_components folder in your root folder. This is the default folder, but we don’t want that (delete that folder and its contents!), it gets too messy, we want to have all our vendors in the same place and that’s exactly why we created the app/scripts/vendor folder before. So we just need a way to tell Bower where we want to place our packages. To do so, we need to create a .bowerrc file in the root of our project specifying the directory to install to. .bowerrc should look like:

{  
  "directory": "app/scripts/vendor"
}

If you run

bower install jquery

again, the jquery files should now have appeared under app/scripts/vendor/jquery.
So far so good. But what if instead of installing every dependency manually, we specify all the dependencies we want in a single file? That’d be awesome right? Let’s do it!

Create a new file called bower.json in the root of your project, and for now, let’s just define our app will depend on jquery, backbone and underscore:

{  
  "name": "skeleton init",
  "version": "1.0.0",
  "dependencies": {
    "jquery": "latest",
    "backbone": "latest",
    "underscore": "latest"
  }
}

“latest”, will install the latest available version. To install them now, simply:

bower install

Now app/scripts/vendor should contain all the files related to jquery, backbone and underscore!

This way, we have a file that contains all the needed dependencies for our app and we can easily install them at once. This makes it very easy to start working with our code in another computer, or to share our code. We don’t even need to save the /vendors in our repository, and that’s why we added such folder to our .gitignore. Things start making sense :)

A couple of commands that might come handy are

bower install  --save

to manually install a package and add it to the bower.json, and

bower uninstall  --save

to uninstall a package and save the changes to the bower.json.

At this point, we need to remember Bower doesn’t take care of the dependencies between packages and that’s what we are now going to use RequireJS for.

Installing RequireJS

RequireJS RequireJS is a module loader we are going to use to take care of the dependencies between the packages we manage with Bower. Let’s install it with:

bower install requirejs --save

Make sure bower.json has been updated too.

Now, in the body of our index.html we can now load requirejs with:

 <script data-main="scripts/main.js" src="scripts/vendor/requirejs/require.js"></script>

Note that in the data-main attribute we have defined which will be the main module of our application, in this case scripts/main.js. Let’s create it!

Go ahead and create a main.js in the scripts folder. Here we need to configure our requirejs defining all the paths to our vendor repositories as well as the dependencies between them.

main.js should look like:

require.config({  
  paths: {
    'jquery': 'vendor/jquery/dist/jquery',
    'underscore': 'vendor/underscore/underscore',
    'backbone': 'vendor/backbone/backbone',
  },
  shim: {
      underscore: {
          exports: '_'
      },
      backbone: {
          exports: 'Backbone',
          deps: ['jquery', 'underscore']
      }
  },
  deps: ['jquery', 'underscore']
});

Now, we can for example say

require([‘backbone’], function(Backbone){...});

and the app will know what the path to backbone will actually be.

Good, we now need something to load to see if this works. Let’s just load our main view. Create an app.js file in a new views folder inside scripts. It should look like:

define(['backbone'], function(Backbone) {  
  var App = Backbone.View.extend({
    initialize: function() {
      console.log( 'Wololo' );
    }
  });

  return App;
});

Note that because this is a Backbone view we need to make sure we require Backbone in our little module.

Finally, we need to boot our new app view adding:

require(['views/app'], function(AppView) {  
  new AppView;
});

to our main.js file. Again, note how we require the view we just created.

Before we try it out, a quick overview:

  1. index.html loads
  2. our <script> tag loads requires
  3. we immediately execute our main module scripts/main.js
  4. main.js will set up some common paths for us so that we can easily require libs
  5. we require views/app.js
  6. app.js requires backbone and creates a dummy view that logs “Wololo”
  7. Then, once that is fully loaded, we reference it with “AppView” back in the main.js and we create a new AppView.

Let’s see if it works in our browser localhost/skeleton/app/ if you don’t use any virtual host.

It worked here!! Wololo!

Few words about RequireJS and its configuration

RequireJS’s primary goal is to encourage modular code. A module is like a traditional script file that avoids polluting the global namespace by receiving its dependencies as arguments to the function that defines the module, thus not needing to create global variables. This enables modules to be loaded as fast as possible, even out of order, but evaluated in the correct dependency order. We use define() to define a module.

When a module has dependencies, the first argument of the define() should be an array of dependency names, and the second, a definition function. The dependencies will be passed to the definition function as function arguments, and they must be listed in the same order as the order in the dependency array. Once all dependencies have loaded, the function will be called to define the module.

On the other hand, we use require() when we need to make sure that the dependencies we pass to the function we want to run are loaded before running it. The require() function is not defining any new modules.

You can read more in detail the differences between define() and require() in this StackOverflow question.

When using require() in our main.js, our top-level script file that does not define a module, we pass a configuration object as the first option. Let’s quickly explain some of the concepts we are using there:

  • paths: Here we have mapped the paths for all the current available modules.
  • shim: Shim configuration enables us to define dependencies between traditional packages that do not use define() to declare dependencies and set a module value. We can also define its exports value and a custom initialization. Exports lets us define the global module value. It lets RequireJS know how to handle non-AMD modules, such as Backbone or Underscore. If we didn’t define it, dependencies in the define block could still be being loaded when the module starts. It basically signals RequireJS when it has stopped loading the resource, so that the modules depending on such resource can start using it.
  • deps: An array of dependencies to load. Using deps is like doing a require() as soon as the loader has processed the configuration.

Installing Marionette.js

MarionetteJS Marionette.js is an upgrade for Backbone, solving some of the major “problems” not tackled by Backbone. In this post you can read about what does Marionette bring onto Backbone explained by Derick Bailey himself, the creator of Marionette.js. With all the setup we have done so far, installing Marionette.js is fairly easy. Marionette requires Backbone, Underscore and jQuery, which we already got, Backbone.Babysitter which offers some advanced views usage, and Backbone.wreqr to help decouple Backbone and Backbone.Marionette application modules and components. Thankfully, Bower already knows about Marionette’s dependencies, and will do everything for us.

Let’s install Marionette with:

bower install marionette --save

Now, we have to update the requirejs configuration in our main.js, which should now look like:

require.config({  
  paths: {
    'jquery': 'vendor/jquery/dist/jquery',
    'underscore': 'vendor/underscore/underscore',
    'backbone': 'vendor/backbone/backbone',
    'backbone.babysitter': 'vendor/backbone.babysitter/lib/backbone.babysitter.js',
    'backbone.wreqr': 'vendor/backbone.wreqr/lib/backbone.wreqr.js',
    'backbone.marionette': 'vendor/backbone.marionette/lib/core/backbone.marionette.js',
  },
  shim: {
    underscore: {
        exports: '_'
    },
    backbone: {
        exports: 'Backbone',
        deps: ['jquery', 'underscore']
    },
    marionette: {
        exports: 'Backbone.Marionette',
        deps: ['backbone']
    }
  },
  deps: ['jquery', 'underscore']
});

require(['views/app'], function(AppView) {  
  new AppView;
});

So, at this point, we have successfully set up all what we need to start developing our js app. However, in the following sections we will set up some tools that will ease the workflow and development of the app.

Few words on Marionette directory structure

There is no standard convention on how to organize your js files within a Backbone.Marionette project. Here I suggest the one that makes more sense to me which is largely based on the bigger tendency found on github’s repositories. My directory structure now looks like:

skeleton
    /app
        /fonts
        /images
        /scripts
            /collections 
            /controllers
            /layouts
            /models
            /routers
            /vendor
            /views
        /styles
        /templates

Note that I put templates inside the app folder as they aren’t js files.

This categorization mostly emanates from the “type” of each js file. An interesting alternative to explore is categorizing by functionality, where each template is right next to the code that includes it.

Grunt

Grunt So we already have a pretty nice app skeleton, but, what if we could improve our workflow by automating some tasks like compiling LESS and minifying the CSS and JS files? Well, that’d be awesome. Grunt will help us to easily accomplish all of this. Grunt is a tool that automates frontend tasks that normally have to be done several times manually, such as minimizing files for production environments, unit testing or even reloading the browser. There is an humongous list of plugins to automate tasks you probably wouldn’t have even thought were automatable!

Installing Bootstrap

To show how useful can Grunt be for us we need something to play with. At the same time, we still need a good tool for interface development. So let’s install Bootstrap.

To install Bootstrap just use Bower as we learnt before:

bower install bootstrap --save

Compiling Bootstrap’s .less files together with some of our own will be a good starting point to learn the power of Grunt.

Setting up the styles structure

Within the styles folder, I have defined:

  • fonts.less : To define all the fonts we will be using in the app.
  • styles.less : To define all the styles of the app.
  • variables.less : To define all the less variables we will have.
  • base.less : Which imports all the previous .less files as well as vendor related .less files such as Bootstrap .less files. For now base.less should look like:
/* Bootstrap */  
@import "../scripts/vendor/bootstrap/less/bootstrap.less";

/* Skeleton */
@import "fonts";
@import "variables";
@import "styles";

Note that we import vendor .less files first so that our own styles will always be at the end of the minified files.
Before we get hands on with Grunt let’s add some dummy styles in our styles.less so that we can later see the minification worked successfully. Let’s just add:

body {  
    background-color: white;
}

I couldn’t come up with anything less useful ;D

Installing Grunt

Ok, time to install Grunt! But wait, Grunt is not really a front-end package, it is more like a tool for development so, if you remember the beginning of this post, we will be using npm instead of bower to manage it.

To install Grunt’s command line interface execute:

npm install –g grunt-cli

npm works similarly to Bower, we can either install each dependency manually, or better, create a package.json where we define all the dependencies. This way, we can quickly get all the dependencies when on a new workspace and we don’t need to have the vendor files in the repository either. Don’t worry, if you recall properly I got your backs covered with .gitignore ignoring the node_modules folder. This is the folder where all the locally installed npm packages will be installed. In this case, the default folder suits us because these scripts are not part of our app.

Let’s now create this package.json in the root of our project, it should look like:

{  
  "name": "skeleton",
  "version": "0.0.1",
  "description": "Skeleton for a Backbone, Marionette application using Require.js, Bower, Grunt and LESS",
  "repository": {
    "type": "git",
    "url": "https://github.com/mezod/skeleton"
  },
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-less": "latest",
    "grunt-contrib-requirejs": "^0.4.4",
    "grunt-contrib-watch": "^0.6.1"
  }
}

Feel free to use the “latest” for all the dependencies. Now to install all the defined dependencies we need to run:

npm install

A new node_modules folder should now just appear in the root of your project with the 4 packages we defined:

  • grunt : grunt itself
  • grunt-contrib-less: For LESS compilation
  • grunt-contrib-requirejs: Integration with RequireJS to use its optimization tool to minify and concatenate the js files
  • grunt-contrib-watch: To watch files for changes

Just so you know, you can alternatively install and uninstall npm packages similarly to how it is done in Bower.

For example, to install grunt:

npm install grunt --save-dev

and to uninstall it:

npm uninstall grunt --save-dev

In both cases, --save-dev will update the package.json configuration.

Configuring Gruntfile.js

We are getting to the magic. It’s time to configure Grunt using the Gruntfile.js configuration file in the root folder. Its basic structure looks like this:

 //Gruntfile  
   module.exports = function(grunt) {

    //Initializing the configuration object
      grunt.initConfig({

        // Task configuration
        less{
          //...
        },
        requirejs{
          //...
        },
        watch{
          //...
        }
      });

    // Plugin loading

    // Task definition

   };

Compiling and minifying the .less files

Before installing Grunt, we defined a very nice stylesheet structure. We now want to use Grunt to compile all the .less files and minify them for us. However, we want to do this into two separate files. As developing with a minified .css file is not the ideal, we will first generate a styles.css in /app/styles/. Additionally, we will create a minified version for the production environment that we will put into /dist. Again, remember we have /dist in the .gitignore because we don’t want to upload these files into the repository.

Before creating the task, let’s not forget to add the necessary code in the <head> of our index.html to load our styles:

 <link rel="stylesheet" media="screen" href="styles/styles.css" type="text/css" />

It’s finally time to configure the less task:

less: {  
   development: {
        options: {
           compress: false,  // no minification in dev
           },
        files: {
           //compiling base.less into styles.css
           "./app/styles/styles.css":"./app/styles/base.less"
           }
        },
   production: {
        options: {
           cleancss: true, // minify css
           // compress: true, // minify css
        },
        files: {
           //compiling base.less into main.min.css
           "./dist/main.min.css": "./app/styles/base.less"
        }
    }
}

Using the examples we can come up with a configuration that suits our needs. For the development, we compile the base.less into the styles.css in our styles folder. For the production, we compile the same .less file but into the main.min.css file in the dist folder in this case. We set cleancss to true to make sure the css is minified.

At the bottom of the Gruntfile.js load the plugin and define the task like so:

  // Plugin loading  
  grunt.loadNpmTasks('grunt-contrib-less');

  // Task definition
  grunt.registerTask('default', ['less']);

Now you can check if it works by running:

grunt less

in your cmd. It should have created both files. Make sure that the code in our styles.less is at the end of both new .css files.

It is also nice to know that if you wish to run only a single task you can run:

grunt less:development

or

grunt less:production

Minimizing the javascript files

Similarly as what we have done for the stylesheets, we now do it for the scripts. We want to generate a single minified .js file. Let’s add the task to our Gruntfile.js. In this case, we will first define a variable compileOptions that will help us configure the requirejs task.
Add the following code before initializing the configuration object:

  // requirejs compile options  
  var compileOptions = {

      mainConfigFile: 'app/scripts/main.js',
      baseUrl: 'app/scripts',
      include: ['main'],
      out: 'dist/main.min.js',
      removeCombined: false,
      findNestedDependencies: true,

      //Removes console.logs for production
      onBuildWrite: function (moduleName, path, contents) {
          if(/(.*)js\/modules\/(.*)/.test(path)) return contents.replace(/console.log(.*);/g, ';');
          return contents;
      }
  }

We have defined our main.js as mainConfigFile and from which it should get all the nested dependencies to minimize. We have also set where do we want the resulting file to be saved, again /dist, which will be our folder for the production files.

Let’s now create the task itself inside the initialization configuration:

requirejs: {  
    compile: {
        options : compileOptions
    }
},

And don’t forget to load the plugin and register the task at the bottom:

  // Plugin loading  
  grunt.loadNpmTasks('grunt-contrib-requirejs');

  // Task definition
  grunt.registerTask('default', ['requirejs']);

Again, to check it works just run

grunt requirejs

We should now have a main.min.js inside /dist with all the vendor js files as well as our app files. Search for “Wololo” to make sure our code is there!

Watching for changes in our files

The tasks we have created so far are useful and help us quite a lot, but we still need to execute them every time. With grunt-watch we can automate these tasks to be executed every time we change the source files. Fasten your seatbelts because here comes the fun!

First, define the new task:

watch: {  
        less: {
            // Watch all .less files from the styles directory)
            files: ['app/styles/*.less'],
            tasks: ['less'],
            // Reloads the browser
            options: {
              livereload: true  
            }
        },
        requirejs: {
            // Watch only main.js so that we do not constantly recompile the .js files
            files: [ 'app/scripts/main.js' ],
            tasks: [ 'requirejs' ],
            // Reloads the browser
            options: {
              livereload: true  
            }
        }
    }

We basically define that if any of the .less files of our /styles folder is modified, watch should call the less task, which will basically recompile and minimize our .less files. We do it similarly with our scripts with the difference that, as we only need such task for the production minified .js, we only execute the task when we modify the main.js. So basically, when you are done with a new feature and want to deploy the new minified .js version to the production environment, simply go to main.js and make a dummy modification so that grunt watch generates your new main.min.js.

Furthermore, you may have noticed the:

options: {  
     livereload: true  
}

We will talk about it immediately. For now just make sure that your Gruntfile.js looks like this:

//Gruntfile  
module.exports = function(grunt) {

  // requirejs compile options
  var compileOptions = {

      mainConfigFile: 'app/scripts/main.js',
      baseUrl: 'app/scripts',
      include: ['main'],
      out: 'dist/main.min.js',
      removeCombined: false,
      findNestedDependencies: true,

      //Removes console.logs for production
      onBuildWrite: function (moduleName, path, contents) {
          if(/(.*)js\/modules\/(.*)/.test(path)) return contents.replace(/console.log(.*);/g, ';');
          return contents;
      }
  }

  //Initializing the configuration object
  grunt.initConfig({

    // Task configuration
    less: {
        development: {
            options: {
              compress: false,  // no minification in dev
            },
            files: {
              //compiling base.less into styles.css
              "./app/styles/styles.css":"./app/styles/base.less"
            }
        },
        production: {
          options: {
            cleancss: true, // minify css
            // compress: true, // minify css
          },
          files: {
            //compiling base.less into main.min.css
            "./dist/main.min.css": "./app/styles/base.less"
          }
        }
    },
    requirejs: {
        compile: {
            options : compileOptions
        }
    },
    watch: {
        less: {
            // Watch all .less files from the styles directory)
            files: ['app/styles/*.less'],
            tasks: ['less'],
            // Reloads the browser
            options: {
              livereload: true  
            }
        },
        requirejs: {
            // Watch only main.js so that we do not constantly recompile the .js files
            files: [ 'app/scripts/main.js' ],
            tasks: [ 'requirejs' ],
            // Reloads the browser
            options: {
              livereload: true  
            }
        }
    }
  });

  // Plugin loading
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Task definition
  grunt.registerTask('default', ['watch']);

};

Note that we are loading the three plugins but registering only the watch task as default. The default task is the one that is executed when we run grunt in the terminal. This task will now make sure to execute the other tasks we have defined whenever appropriate.

LiveReload

I owe you an explanation. When configuring our watch task we added:

options: {  
    livereload: true  
}

Setting livereload to true will automatically reload our browser every time we save one of the watched files. How great is that?

For it to work, you have to install a LiveReload plugin for your browser. I use Firefox and thanks to this StackOverflow question I found this plugin that works sweet.

Don’t forget to enable the plugin in your browser once you have installed it. Now to try it out, just go to our styles.less file and modify the background-color to black. Save the file, and let grunt do the rest. If you now go to your browser, your empty app should now display a black background without needing to refresh the page!

What’s next?

So, we have covered a hell of a lot of stuff. Now we have a pretty nice skeleton to start working with our Backbone.Marionette app, to manage its dependencies and to have a nice workflow. But as always, there’s always more. Topics we didn’t cover include: templating, testing and automated testing with grunt, environment definition and deployment. There are plenty of tools out there to help you out if you need something more advanced.

So, have fun building something new. I hope this post helped you somehow. If you find any issues or have any problems, I’d be glad to hear about them! Cheers!

comments powered by Disqus