Javascript Distribution in a galaxy far, far away

@opherv / FrontEnd.IL / Dec 2017

PackWars

And the winner is...

Require.js
 

SystemJS
 

Browserify
 

Webpack
 

Hi! I'm Opher

I'm a creative developer at Eko

 

What's packing/bundling and why do we need it?

What if the

were to be built as Web App?

Death Star

<script src="superLaser.js"></script>
<script src="turboLasers.js"></script>
<script src="garbageCompactor.js"></script>
<script src="communication.js"></script>

Loading modules manually

<link rel="stylesheet" href="style/superLaser.css">
<link rel="stylesheet" href="style/turboLaser.css">
<link rel="stylesheet" href="style/garbageCompactor.css">
<link rel="stylesheet" href="style/communication.css">
<link rel="stylesheet" href="style/style.css">
DeathStar.html

@opherv

Loading modules manually - advantages

No new knowledge required

@opherv

You can code like it's 1999

@opherv

Loading modules manually - disadvantages

Module Communication via globals

Huge list to manage in one file

Sorting out dependencies gets difficult

Multiple HTTP calls

@opherv

Doing this might look like a good idea at first...

sometimes they don't turn out like you imagined

Quite Ancient

Can teach us a thing or two

Packs a punch

Uses odd syntax and cryptic messaging

Uncaught Error: Mismatched anonymous define() module: function definition(name, global)

~2009 by James Burke

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

Using Require.JS

DeathStar.html
require(['SuperLaser'], function(superLaser) {
    superLaser.destroyPlanet("Alderaan");
});
DeathStar.js

@opherv

//dependencies
define(['BeamCrystal'], function (beamCrystal) {

    ...

    // private function
    function prepareLaser(){
    }

    // public function
    function destroyPlanet(planet){
        prepareLaser(beamCrystal);
        laser.fire(planet);
    }

    // exposed public object
    return {
        destroyPlanet: destroyPlanet
    }
});

Anatomy of an AMD Module

SuperLaser.js

@opherv

Multiple HTTP calls

* HTTP/1

@opherv

Multiple HTTP calls

* HTTP/1

@opherv

Require.JS - advantages

sync & async resource loading

Uglifying & optimizing

Require.JS - disadvantages

Long and winding configuration file

Confusing documentation

Giant JS file at the end - downside of bundling

@opherv

You have to write AMD modules

@opherv - follow me on twitter!

GIVEAWAY

TIME

{} + [] = 
0
[] + {} = 
[Object Object]

Both break down into smaller pieces

Transform one language into another

~2012 by James Halliday

<script src="scripts/bundle.js"></script>

Using Browserify

DeathStar.html

@opherv

Building with Browserify

browserify DeathStar.js -o bundle.js
run
var superLaser = require("SuperLaser");

superLaser.destroyPlanet("Alderaan");
DeathStar.js
bundle.js
--- compiled code ---

@opherv

//dependencies
var beamCrystal = require('BeamCrystal');

...

// private function
function prepareLaser(){
}

// public function
function destroyPlanet(planet){
    prepareLaser(beamCrystal);
    laser.fire(planet);
}


// exposed public object
module.exports = {
        destroyPlanet: destroyPlanet
    }

Anatomy of a Common.js Module

SuperLaser.js - AMD

@opherv

//dependencies
define(['BeamCrystal'], function (beamCrystal) {

    ...

    // private function
    function prepareLaser(){
    }

    // public function
    function destroyPlanet(planet){
        prepareLaser(beamCrystal);
        laser.fire(planet);
    }

    // exposed public object
    return {
        destroyPlanet: destroyPlanet
    }
});
SuperLaser.js - CommonJS

Browserify Transforms

Babelify - turn ES6+ code into vanilla ES5 

 

Cssify - allows you to require CSS files

 

Uglifyify - minify code

browserify DeathStar.js -o bundle.js -t babelify
run

@opherv

Browserify - advantages

Simple to set up and lightweight

Browserify - advantages

Use NPM modules in both server and client

disadvantages - multiple bundles

@opherv

Browserify - disadvantages

No asynchronous require

Relies on CommonJS packages

Giant JS file / bundles with repeated code

@opherv

Gets the job done, in its own way

Art by TheAdamTaylor

Comes in two flavors - old and new

Shies away from commitment

~2012 by Tobias Koppers

Webpack 1.0

Webpack >=2.0

<script src="scripts/bundle.js"></script>

Using Webpack

DeathStar.html
require("./style.css");

var superLaser = require("SuperLaser");

superLaser.destroyPlanet("Alderaan");
DeathStar.js

@opherv

import styles from "./style.css";

import superLaser from "./SuperLaser";

superLaser.destroyPlanet("Alderaan");
DeathStar.js ES6 (for cool kids)
//dependencies
import beamCrystal from 'BeamCrystal'

...

// private function
function prepareLaser(){
}


// public function
function destroyPlanet(planet){
    prepareLaser(beamCrystal);
    laser.fire(planet);
}


// exposed public object
export default {
        destroyPlanet: destroyPlanet
    }

Anatomy of an ES6 Module

SuperLaser.js

@opherv

Building with Webpack

webpack
run
module.exports = {
    entry: "./DeathStar.js",
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            { test: /\.css$/, loader: "style!css" }
        ]
    }
};
webpack.config.js
bundle.js
-- compiled code --

@opherv

Webpack Code Splitting

require.ensure(["SuperLaser"], function(require) {
    var superLaser = require("SuperLaser");

    superLaser.destroyPlanet("Alderaan");
});

DeathStar.js
webpack
run
bundle.js / bundle.1.js / bundles.2.js ....

@opherv

import('SuperLaser').then(function(SuperLaser) {
    superLaser.destroyPlanet("Alderaan");
}).catch(function(err) {
    console.log('SuperLaser Malfunction!', err);
});

Static Analysis

@opherv

Webpack - advantages

Loads AMD, CommonJS and ES6

Sync and Async loading

Automatic bundle splitting

Static Analysis

Imports assets (CSS/Images/whatever)

@opherv

Tree Shaking (Webpack >=2.0)

Webpack - disadvantages

Can't load non-webpacked bundles

Documentation feels like it was google-translated from a wiki page in Japanese 
(docs on v>=2 are much much better!)

More complex to setup

@opherv

@opherv on Twitter. It is your destiny.

GIVEAWAY TIME

typeof NaN == 
"number"

New to the story

SystemJS

Comes with something that handles packages

Doesn't need your help

~2013 by Guy Bedford

HTTP/2  | SPDY

HTTP/1

@opherv

<script src="https://jspm.io/system.js"></script>
<script>
    System.import("github:DarthVader/SuperLaser").then(function(SuperLaser) {
       SuperLaser.destroyPlanet("Alderaan");
    });
</script>

Using SystemJS

DeathStar.html

jpsm.io CDN allows you to use jspm, github and npm sources

 

Sources are sent minified over HTTP/2

@opherv

<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
  System.import('DeathStar.js');
</script>

Using SystemJS

DeathStar.html
jspm install
run
creates config.js and jspm_packages/system.js

You have the option of bundling, code splitting or using an HTTP/2 CDN

@opherv

SystemJS - advantages

Potentially performant with no bundling needed!

No bundling - no bundle management issues

Can still bundle using JSPM Builder

Load directly from jspm.io/npm/Github

@opherv

Tree shaking (with rollup)

a

SystemJS - disadvantages

Using two package managers (kinda)

Might rely on a polyfill (es6-module-loader)

Requires HTTP/2 support in the browser/server

Requires deeper understanding of packaging/bundling

@opherv

A tiny reminder

I could not possibly cover all features and caveats

 

Eventually, all of these are tools that get the job done

 

Pick whatever YOU think is right, and makes you happy

@opherv

Don't be afraid of learning

Hate leads to flame wars

                             on GitHub.”

Title Text

Thanks!

@OpherV

opherv.com

PackWars: JS Distribution in a galaxy far, far away | FrontEnd.IL

By Opher Vishnia

PackWars: JS Distribution in a galaxy far, far away | FrontEnd.IL

  • 1,521