ES2015 Stack

Using JSPM and Backbone

Jordane Grenat | @jgrenat

ES2015 Modules

export function name1() {

};

export const name2 = 'value';

export let name3 = 1;

export class MyClass {};


let name4 = () => {};
let name5 = 5;

export { name4, name5 };

export { name4 as arrowFunc, name5 as five };

Named Exports

export default 'value';

Default Export

import theDefaultExport from 'lib';

import { name1, name2 } from 'lib';

import theDefaultExport, { name1 } from 'lib';

import { name1 as otherName } from 'lib';

import * as myLib from 'lib';

import 'lib';

Imports

Module Loader API

  • Dynamically declare and import modules


     
  • Configure how modules are imported

Dynamic Imports

System.import('lib')
    .then(myLib => {
        // Code
     })
    .catch(error => {
        // Error
    });

Configure imports

System.config({
  "map": {
    "backbone": "npm:backbone@1.2.1",
    "core-js": "npm:core-js@0.9.18",
    "jquery": "github:components/jquery@1.11.3",
    "underscore": "npm:underscore@1.8.3", 

    "github:jspm/nodelibs-http@1.7.1": {
      "url": "github:jspm/nodelibs-url@0.1.0",
      "util": "github:jspm/nodelibs-util@0.1.0"
    },

    "npm:core-js@0.9.18": {
      "fs": "github:jspm/nodelibs-fs@0.1.2"
    }
  }
});

SystemJS

SystemJS is an

universal module manager

It can load:

define(['jquery'], function($) {
    return function() {};
});

AMD Modules

var $ = require('jquery');
exports.myModule = function () {};

CommonJS Modules

var $ = require('jquery');
exports.myModule = function () {};

ES2015 Modules

window.myModule = function() {};

(Awful) Global Modules

You don't even CARE what kind of module you're loading!

With plugins, you can also load:

  • CSS
  • Text
  • JSON
  • Images
  • Markdown
  • JSX
  • Fonts
  • ...

Polyfill for the
ES2015 Module Loader

<script src="system.js"></script>
<script>
  // loads relative to the current page URL
  System.import('./local-module.js'); 

  // load from an absolute URL directly
  System.import('https://code.jquery.com/jquery.js');
</script>

Package manager

built on top of SystemJS

It can load packages
from any registry:

  • NPM
  • Bower
  • Github
  • JSPM
  • ...
jspm install npm:aurelia

jspm install github:twbs/bootstrap

jspm install backbone

Generates SystemJS config automatically

Offers a CLI to bundle and unbundle our modules

(but we'll see that later...)

Front MVC framework with

4 kinds of elements

Template management is left to the developper

We're gonna use

Let's start
our project!

Setup NPM
and JSPM

npm init
# One global install for the CLI tools
npm install -g jspm

# One local install for the project
npm install --save-dev jspm

# Then we can init our jspm project
jspm init
# Add a prefix for jspm config into package.json. Let the default.
Would you like jspm to prefix the jspm package.json properties under jspm? [yes]:

# The default public folder path. Type "src".
Enter server baseURL (public folder path) [./]:src

# The folder in which our modules will be installed. Let the default.
Enter jspm packages folder [src/jspm_packages]:

# The systemJS config file path, let's type src/jspm-config.js.
Enter config file path [src/config.js]:src/jspm-config.js

# Let the default (yes).
Configuration file src/jspm-config.js doesn't exist, create it? [yes]:

# URL from the browser from where our website will be served. Let the default.
Enter client baseURL (public folder URL) [/]:   

# We're going to use Babel. Let the default.
Which ES6 transpiler would you like to use, Traceur or Babel? [babel]:

JSPM Init:
the Questions

Our project is initialized!

Our application

A webpage that displays random quotes

jspm install backbone jquery
{
    "quotes": [
        {
            "text": "I think the worst time to have a heart attack is during a game of charades.",
            "author": "Demitri Martin"
        }, {
            "text": "When people ask me how many people work here, I say, about a third of them.",
            "author": "Lisa Kennedy Montgomery"
        }, {
            "text": "Always and never are two words you should always remember never to use.",
            "author": "Wendell Johnson"
        }, {
            "text": "Those who believe in telekinetics, raise my hand!",
            "author": "Kurt Vonnegut"
        }, {
            "text": "Always go to other people's funerals, otherwise they won't come to yours.",
            "author": "Yogi Berra"
        }, {
            "text": "Knowledge is knowing a tomato is a fruit; wisdom is not putting it in a fruit salad.",
            "author": "Miles Kington"
        }
    ]
}

src/config.json

<!DOCTYPE html>
<html lang="fr">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>Random Quotes</title>

    <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.7/es5-shim.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.7/es5-sham.min.js"></script>
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>

<body>

    <div class="view-main"></div>
	


    <script src="/jspm_packages/system.js"></script>

    <script src="/jspm-config.js"></script>
    <script>
        System['import']('../startup');
    </script>

</body>

</html>

src/index.html

1- Load SystemJS

2- Load our SystemJS config

3- Import our entry point

(use ['import'] for IE8 compatibility)

import Backbone from 'backbone';

export default Backbone.Model.extend({

    defaults: {
        text: '',
        author: ''
    }

});

src/quote/QuoteModel.js

import Backbone from 'backbone';

import config from 'config.json!';
import Quote from 'quote/QuoteModel';


export default Backbone.Collection.extend({

    model: Quote,

    initialize() {
        this.set(config.quotes);
    }

});

src/quote/QuoteCollection.js

jspm install json

Load JSON config file

<blockquote class="quote">
	{{ quote.text }}
	<cite class="quote-author">{{ quote.author}}</cite>
</blockquote>

<button class="js-newQuote quoteButton">Another quote!</button>

src/quote/quoteTemplate.hbs

jspm install handlebars

html, body {
  height: 100%;
}

body {
  background-color: #daf0f8;
}

.view-main {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
}

.quote {
  width: 60%;
  margin: 0;
  text-align: center;
  font-family: Georgia, serif;
  font-size: 3rem;
  font-style: italic;
  line-height: 1.45;
  color: #383838;
}

@media (max-width: 1200px) {
  .quote {
    width: 90%;
  }
}

.quote-author {
  color: #999999;
  font-size: 1.5rem;
  display: block;
  margin: 1rem 0;
  text-align: center;
}

src/quote/quote.css


.quote-author:before, .quote-author:after {
  content: "\2009 \2014 \2009";
}

.quoteButton {
  position: relative;
  margin-top: 2rem;
  min-height: 3rem;
  padding: 0.4rem 2rem;
  border: 4px solid lightblue;
  background: none;
  border-radius: 20px;
  font-size: 1.5rem;
  font-family: Arial, serif;
  font-style: italic;
}

.quoteButton:hover, .quoteButton:focus {
  background-color: lightblue;
}

.quoteButton:active, .quoteButton:focus {
  outline: none;
  border: 4px solid lightblue;
}

.quoteButton:active:before, .quoteButton:focus:before {
  content: ' ';
  border-radius: 20px;
  position: absolute;
  border: 2px dashed white;
  display: block;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

import Backbone from 'backbone';

import 'quote/quote.css!';
import renderTemplate from 'quote/quoteTemplate.hbs!';
import QuoteCollection from 'quote/QuoteCollection';


export default Backbone.View.extend({

    el: '.view-main',

    events: {
        'click .js-newQuote': 'render'
    },

    initialize() {
        this.quotes = new QuoteCollection();
    },

    render() {
        let quote;
        while((quote = this.quotes.sample()) && quote === this.quote);
        this.quote = quote;

        this.$el.html(renderTemplate({ quote: quote.toJSON() }));
    }

});

src/quote/QuoteView.js

jspm install css
jspm install hbs=github:davis/plugin-hbs

1- Load CSS file

2- Load template

3- Select random quote

4- Render template

'quote/quote.css!css'

import Backbone from 'backbone';

import QuoteView from 'quote/QuoteView';

let AppRouter = Backbone.Router.extend({

    routes: {
        '': 'quote'
    },

    initialize() {
        this.quoteView = new QuoteView();
    },

    quote() {
        this.quoteView.render();
    }

});

export default new AppRouter();

src/appRouters.js

import Backbone from 'backbone';
import 'appRouter';

Backbone.history.start();

startup.js

Let's industrialize our project!

Installation

Very easy, we only need to install npm and JSPM modules

{
  "scripts": {
    "postinstall": "jspm install"
  }
}
npm install

package.json

Build

SystemJS is loading

many files
(= far too many requests)

jspm install npm:clean-css

Let's bundle them!

jspm bundle startup scripts.js --inject
  1. Create a bundle file with startup and its dependencies
     
  2. Store the result into scripts.js
     
  3. Inject the bundle configuration into our jspm-config.json
jspm bundle startup src/scripts.js --inject --minify

You can (and should) 
even minify it

New bundle section
in our jspm-config.js

{
  "bundles": {
    "scripts": [
      "npm:underscore@1.8.3/underscore",
      "github:components/jquery@2.1.4/jquery",
      "npm:process@0.10.1/browser",
      "quote/quote.css!github:systemjs/plugin-css@0.1.13",
      "github:components/handlebars.js@3.0.3/handlebars",
      "config.json!github:systemjs/plugin-json@0.1.0",
      "quote/QuoteModel",
      "npm:underscore@1.8.3",
      "github:components/jquery@2.1.4",
      "npm:process@0.10.1",
      "github:components/handlebars.js@3.0.3",
      "quote/QuoteCollection",
      "github:jspm/nodelibs-process@0.1.1/index",
      "quote/quoteTemplate.hbs!github:davis/plugin-hbs@1.0.0",
      "github:jspm/nodelibs-process@0.1.1",
      "quote/QuoteView",
      "npm:backbone@1.2.1/backbone",
      "appRouter",
      "npm:backbone@1.2.1",
      "startup"
    ]
  }
}

Result: 4 files loaded

But now it only loads the compiled file, how can I do when I'm coding?

jspm unbundle

(Remove all bundle config from the jspm-config.js file)

And if I want to load some files only when needed ?

=> Use System.import() and create several bundles !

jspm bundle "startup" src/global.js --inject --minify

jspm bundle "quote/* - startup" src/quotes.js --inject --minify 
quote() {
    let promise;
    if(this.quoteView) {
        promise = Promise.resolve(this.quoteView);
    } else {
        promise = System.import('quote/QuoteView').then(QuoteView => {
            this.quoteView = new QuoteView['default']();
            return this.quoteView;
        });
    }

    return promise.then(quoteView => quoteView.render());
}

src/appRouter.js

Our build

  1. Bundle our scripts
  2. Clean dist directory
  3. Copy systemJS and index.html files
  4. Copy images files (even if there is none there)
  5. Move bundle files
  6. Unbundle our scripts

Gulp? Grunt? Broccoli?

Why not NPM ?

Our script

npm install -D rimraf cpy node-mv  # Universal tools
jspm bundle startup src/modules/global.js --inject --minify
jspm bundle 'quote/**/* - startup' src/modules/quote.js --inject --minify
rimraf dist
cpy src/jspm_packages/*.* dist/jspm_packages
cpy src/index.html src/jspm-config.js dist
cpy **/*.png **/*.gif **/*.jpg ../dist --parents --cwd=src
cpy src/modules/**/* dist/modules
rimraf src/modules
jspm unbundle

src/scripts/build.js

(Don't forget to update the file chmod)

Our npm scripts

{
  "scripts": {
    "build": "./src/scripts/build.sh"
  }
}
npm run build

package.json

Serve

What we want

  • Serve our files

     
  • Auto-refresh our browser

2 in 1: browser-sync

browser-sync start --files 'src/**/*' --server 'src'
npm install --save-dev browser-sync

2 in 1: browser-sync

{
  "scripts": {
    "build": "sh ./src/scripts/build.sh",
    "start": "jspm unbundle && browser-sync start --files 'src/**/*' --server 'src'"
  }
}
npm start

package.json

Tests

Copy of ES2015 Stack with JSPM and Backbone

By social4hyq

Copy of ES2015 Stack with JSPM and Backbone

  • 1,037