Bootstrap an enterprise SPA with continuous integration

Intro

I evaluated SailsJS with the hope of reducing time spent and establishing consistency when setting up an environment for a web project. Some best practices that can translate into a robust and stable enterprise web platform is a task management tool, a NodeJS powered web container, package manager, continuous integration, automated unit testing, agreeing on a consistent pattern to generate web services, request routing, and a relevant front-end strategy thats bundled with the project(i.e. Angular, Ember, etc). I created a repo that’s a culmination of brilliant web-based open-source products geared towards bootstrapping a web-platform, that leverages Node, NPM and SailsJS to bring it all together.

Installing Sails

Before proceeding with the below steps, you’ll need NodeJS and bower installed.
To install the latest stable release with the command-line tool:

sudo npm -g install sails

On Windows (or Mac OS with Homebrew), you don’t need sudo:

npm -g install sails

Create a new app:

sails new testProject

Now lift the server:

cd testProject
sails lift

At this point, if you visit (http://localhost:1337/) you will see the default home page.

Let’s do some really cool stuff.

Sails has a concept called generators that allow developers to extend and create custom solutions to fit their needs. This project’s Sails custom generator uses ReactJS as the toolset to create front-end components, and plugged in other battle-tested open source products to cover testing, web asset management and continuous integration.

You’ll need to configure Sails to use this extension for creating new Sails sites. This can be achieved by creating a file named .sailsrc in your home directory, or the parent directory of where you are creating projects.

{
    "generators" : {
        "modules" : {
            "frontend" : "sails-generate-reactjs"
        }
    }
}
npm -g install sails-generate-reactjs

Create a new app using the custom sails generator:


sails new  --force
cd 
npm install
sails lift

The new site(http://localhost:1337/) will be a site that’s scaffolded with ReactJS and Bootstrap

What’s Sails?

SailsJS is a battle-tested lightweight and flexible Javascript framework built on top of NodeJS. Sails follows an MVC-based architecture which resembles Ruby on Rails. HTTP routing is managed via Express and Sails sits on top of socket.io for Websocket communication. The framework provides a foundation, that simplifies the process of creating web-based applications. Offering one do-it-all language/framework where it’s principles rules above all, is not always realistic. Sails is front-end agnostic, and empowers developers to use Sails as a foundation that’s compatible with any front-end strategy(Angular, Backbone, ReactJS, Ember). For this repo we chose ReactJS, for reasons that we’ll dive into shortly.

Sails ships with GRUNT, which means that your app’s front-end asset workflow is fully customizable. The example discussed below comes packaged with a task to transcompile ES6 components into ES5 modules via Babel.

Sails also comes bundled with Waterline, which is a powerful ORM that provides adapters to a multitude of data store platforms, such as MongoDB, MySQl, Oracle, Postgres and Redit. Waterline provides a unified API to interface with data related operations from different kinds or databases or 3rd party API’s. Integrating a waterline adapter within the framework mentioned below for Azure blob storage is on the development horizon.

Sails also offers a feature called blueprints, which you can auto-generate RESTful JSON web API’s via their command line interface, without writing a single line of code. Blueprint based API’s support all CRUD operations(create, read, update, destroy, search).

This project also comes packaged with the latest version of Bootstrap 3 and the Sass CSS pre-processor.

What’s React, and why should I use it?

React is a Javascript library for building user interfaces, while focusing on building reusable declarative components that know how to render themselves into the DOM. The library is a super simple concept, and is primarily focused on controlling how/when front-end components are rendered onto devices with minimal amount of DOM mutations as possible.

Components in other front-end patterns such as Angular, are created as directives and transclutions. React is not an MVC framework and is more than just the V in MVC. React approaches interface design differently from MVC-like frameworks by breaking things into reusable components, as opposed to a Model and Controller. React is not opinionated on how your components should fetch your data from the backend, and plays nicely with your stack, whatever it may be.

What makes React Perform so well?

Designing front-end interfaces is hard, due to the high number of state changes taking place. Data changing over time is the root of all evil when it comes to performance, as writing to the DOM results in expensive operations.

React’s main selling point is it’s performance and low memory footprint, as component rendering is fast and responsive due to its unidirectional data flow for mutated components. React versions all component change written to the browser within a virtual DOM. React elements in it’s virtual DOM are light, stateless, immutable, virtual representation of a DOM element that was rendered. When the state of an component changes, React decides whether a DOM update should occur by creating a new sub tree on the virtual DOM and diffs it with the old one. The minimal set of DOM mutations are computed and placed in a queue, which are then batch executed. This unidirectional data flow leaves you with a linear increase in complexity, as you’re able to isolate your complexity into separate components which also makes unit testing easier. The most performance heavy operation in React is the step writing to the DOM, its not in React’s reconciliation step. The virtual DOM principle of React was inspired from the Doom 3 rendering engine. React also supports rendering to the canvas to utilize the browsers GPU for smother animations and transitions.

To recap, anytime state changes for a component, the entire components is re-rendered while react uses its V-Dom recon algorithm to decide which DOM elements need to be updated to the client. No DOM lookup, no magical two way data binding, no model dirty-checking. Everything is Immutable and declarative.

JSX

Facebook created an Javascript extension markup language called JSX. The basic idea is to use an intuitive syntactic sugar to write React components and switch in between your markup and your JS objects(by using a parenthesis indicator in your JSX file). JSX is optional and is intrinsic to the React framework. If you looked at the source code for React you’ll find zero references to JSX. Honest opinion, very awkward and uncomfortable at first, but it rocks once you get used to it. JSX files must be converted to regular javascript files to be used. Our Sails framework automatically takes care of that for you, through a Grunt task called Browserfy. We’ll dive into Browserfy in more detail shortly.

A Sample React Component

LikeButton.jsx

var LikeButton = React.createClass({
  getInitialState: function() {
    return {liked: false};
  },
  handleClick: function(event) {
    this.setState({liked: !this.state.liked});
  },
  render: function() {
    var text = this.state.liked ? 'like' : 'haven\'t liked';
    return (
<p onClick={this.handleClick}>
        You {text} this. Click to toggle.
</p>
);
  }
});

React.render(
  <LikeButton />,
  document.getElementById('example')
);

The Render function will be called anytime the state(in this case the liked property) of the component changes. React also allows you to override the lifecycle events of a component, as we’ve done above with the setInitialState function. You can customize the decision for when a component should re-render on state change, by overriding the shouldComponentUpdate function. All lifecycle events that are available for overriding are referenced in their API Docs.

The component is initially mounted onto a DOM element by the final call to React.render, which takes 2 arguments. The first is the React component, and the second is a reference to a DOM element. The first argument must be the root element of the component, as React will be serializing this component to its Virtual DOM which is a tree data structure.

index.htm

<html>
  <head>
    <script src="build/react.js"</script>
    <script type="text/jsx" src="build/LikeButton.js"></script>
  </head> 

<div id="example"</div>

</html>

Seems simple, right? React reduces coupling as components are 100% pure Javascript, self contained and have a single responsibility. There’s no longer a need to tie controllers with models and tie the models to the rendering logic above.

Components in ES6

ES6 feature adoption across the industry is growing at a rapid pace, due to the benefits of inheritance, REST parameters, pure native data collections(sets, maps, iterators), proxies, constants, object destructing, etc. React supports components written in ES6. For ES6 best practices I would encourage you to take a look at the JS guide AirBnB wrote up on ES6.

React now supports components written in ES6. I’ve included an example below to illustrate an ES6 React component class.

import React from 'react';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (

<div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
</div>

    );
  }
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };

module.exports = Counter;

The challenge with ES6 is that most browsers are unable to support the syntax. Our project uses a framework called BabelJS to automatically take care of that for you. BabelJS is a transcompiler that converts ES6 Javscript files into ES5 friendly code. There are other ES6 compilers out there like Traceur, but Babel is the most trusted and found it easier to use and produces more efficient code.

Our project comes packaged with a ES6 components folder under /api/components/es6, which is transpiled into ES5 by a grunt file change watch task. The directory location is configurable and can be managed in Sails pipeline file at /tasks/pipeline.js.

Browserify = Minification / JS Dependency management = Reduced page loadtime

Browserify is the most widely used and trusted tool that merges all your dependent node.js modules into a single JS file. Browserify has a Grunt compilation task, that traverses all your JS files, figures out the dependencies and concatenates them into a single JS file, which you then into your HTML. Tired of looking at the laundry list of JS file includes in your main layout.html page? This project comes bundled with the browserify build step, that runs anytime your JS source files change.

Grunt provides a task for browserify, which we’re using. Here’s the Grunt configuration

module.exports = function(grunt) {

  var version = grunt.file.readJSON('package.json').version;
  var pipeline = require('../pipeline');

  grunt.config.set('browserify', {
        js: {
          src : pipeline.browserifyMainFile,
          dest: '.tmp/public/browserify/debug.' + version + '.js'
        },
        options: {
          transform: [require('grunt-react').browserify],
          basedir: pipeline.appRootDir
        }
  });

  grunt.loadNpmTasks('grunt-browserify');
};

We tell Sails which file is our main applications JS file, within the pipeline config file. Browserify recursively crawls through all referenced dependencies and Node modules used, then concatenates and outputs the Javascript code out into a single JS file. This one JS file is included in our layout.ejs file, and contains our entire Javascript project.

<script src="/browserify/debug.0.0.0.js"></script>

There is also an option in the browserify grunt task called ‘uglify’, which takes care of the minify step

uglify: {
      options: {
        banner: '/*! Grunt Uglify <%= grunt.template.today("quot;yyyy-mm-dd") > */ '
      },
      build: {
        src: 'bundle.js',
        dest: 'bundle.min.js'
      }
    }

This blog does a good job expressing the importance to minify the css and JS assets of your web app.

This is still a young project, but daily commits are being made. Pull-requests are welcomed, and contributors are appreciated:)

Thank you for reading.

Erik Schlegel

Disclaimer: The views and opinions expressed in this article are those of the author and do not necessarily reflect the official policy or position of Microsoft.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: