Modules in the Browser. – Finally. – What are modules



Modules in the Browser. – Finally. – What are modules

0 0


modules-in-the-browser

Presentation on Browser modules

On Github matthewp / modules-in-the-browser

Modules in the Browser.

Finally.

By Matthew Phillips of Bitovi

What are modules

  • Unit of code useable by other modules.
  • Ask what they think modules are.
  • How modules have evolved.
  • Why we need modules defined at the spec level.
  • How to use ES modules.
  • How to hack the loader.

The beginning

index.html

<script src="jquery.js"><script/>
<script src="jquery.datepicker.js"><script/>
<script>
$(function(){

  $("#date").datePicker();

});
</script>
            

app.js

window.app = {};
            

math.js

app.math = {
  add: function(a, b) { return a + b; },
  subtract: function(a, b) { return b - a; }
};
            

dates.js

var math = app.math;

app.dates = {
  compare: function(a, b) { return math.subtract(a, b) > 0; }
};
            

index.html

<script src="jquery.js"></script>
<script src="jquery.datepicker.js"></script>
<script src="app.js"></script>
<script src="math.js"></script>
<script src="dates.js"></script>
<script src="main.js"></script>
            

index.html

<script src="jquery.js"></script>
<script src="jquery.datepicker.js"></script>
<script src="app.js"></script>
<script src="math.js"></script>
<script src="utils.js"></script>
<script src="dates.js"></script>
<script src="tabs.js"></script>
<script src="main.js"></script>
            
Module Depends On Is dependency jquery.datepicker.js jquery.js main.js app.js

math.js

dates.js

math.js utils.js dates.js dates.js math.js main.js tabs.js

utils.js

jquery.js

main.js main.js

dates.js

jquery.js

jquery.datepicker.js

tabs.js

Problems

  • Order matters, have to manage this manually.
  • Circular dependencies happen.

Module Loaders!

RequireJS, curl.js, StealJS

main.js

define([
  "jquery",
  "math",
  "utils",
  "jquery-datepicker"
], function($, math, utils){
  ...
});
            

Solves

  • Don't have to care about order, just declare dependencies in each module.
  • Circular dependencies are possible.

Problems

  • Usually requires configuration of where modules are located.
  • People hate wrapping their code.

Node.js

Using modules becomes fun.

config.js

var fs = require("fs");

module.exports = function(){
  var json = fs.readFileSync("config.json", "utf8");
  return JSON.parse(json);
};
            

main.js

var config = require("config");

// Yay I have config!
            

CommonJS!

  • Component
  • Browserify
  • WebPack
  • Duo

Solves

  • Less configuration (depends on NPM for that)
  • No wrapping of code.

Problems

  • No wrapper means pre-compilation.
  • Usually means build scripts. This is no fun for hacking.
  • Solutions have similiar, but different APIs.
  • require() must be parsed statically. Can't do require("foo" + "bar")

Why ES Modules

Least common denominator

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['exports', 'b'], function (exports, b) {
            factory((root.commonJsStrictGlobal = exports), b);
        });
    } else if (typeof exports === 'object') {
        // CommonJS
        factory(exports, require('b'));
    } else {
        // Browser globals
        factory((root.commonJsStrictGlobal = {}), root.b);
    }
}(this, function (exports, b) {
    //use b in some fashion.

    // attach properties to the exports object to define
    // the exported module properties.
    exports.action = function () {};
}));
            

To be safe, have 0 dependencies

Syntax

  • Statically declared
  • Dynamically loaded
ECMAScript 6 modules: the final syntax

export

math.js

export default function(a, b){
  a + b;
};
            

import

import add from "math";

add(1, 2); // -> 3

export default function(){
  ...
};
            

export

math.js

export function add(a, b){
  return a + b;
};

export function subtract(a, b){
  return b - a;
};
            

import

import { add, subtract } from "math";

add(1, 2); // -> 3
subtract(1, 3); // -> 2
            

import

import * as math from "math";

math.add(1, 2); // -> 3
math.subtract(1, 3); // -> 2
            

What static imports mean

  • Module is parsed for dependencies before execution.
  • Dependencies are loaded and executed.
  • This would not be possible without new syntax.

Dynamic imports

Loading modules on-demand.

System.import

var pageNum = getPageNumber();
System.import("page-" + pageNum).then(page => {

  // do something with page

});
            

Possibility

const $ = await import.default("jquery");
const { ajax } = await import.namespace("jquery");
            

What is a Loader?

System for loading and executing modules. Dependency manager. Module registry.

WhatWG Loader

  • Spec whatwg.github.io/loader
  • Polyfill github.com/ModuleLoader/es6-module-loader

Loader

  • The Loader constructor is used to create new loaders.
  • Doesn't do much by default.

System loader

  • Default Browser Loader.
  • Loads <module>s
  • Can be ignored.
  • Can be extended.

Provides hooks to extend the loading process.

normalize -> locate -> fetch -> translate -> instantiate

  • These are just functions on the Loader prototype.
  • normalize/locate might because resolve

Loader Pipeline

import math from "./math"; normalize: ./math -> utils/math locate: utils/math -> http://example.com/utils/math.js fetch: http://... -> "export function add(a, b){ ... " translate: "export function .." -> "export function .." instantiate: "export function .." -> { deps: [], execute: fn }

main.js

import math from "./math";
            

Loader does...

// Step 1
loader.normalize("./math", "app/main", "http://example.com/app/main.js");

// Step 2
loader.locate({ name: "utils/math" });

// Step 3
loader.fetch({
  name: "utils/math",
  address: "http://example.com/utils/math.js"
});

// Step 4
loader.translate({
  name: "utils/math",
  address: "http://example.com/utils/math.js",
  "source": "export function add(){ ..."
});

// Step 5
loader.instantiate({
  name: "utils/math",
  address: "http://example.com/utils/math.js",
  "source": "export function add(){ ..."
});
            

Normalize

Generate a module id

  • Given an identifier: ./bar
  • Create a key: foo/bar

Examples

Locate

Determine its url

  • Given a module name: foo/bar
  • Locate it: http://example.com/foo/bar.js

Examples

Fetch

As it sounds

  • Given a url: http://example.com/foo/bar.js
  • Fetch the source code
  • XHR, WebSockets

Examples

Translate

Source to source

  • Given source code.
  • Translate into more source.

Examples

Instantiate

Provide value

  • Define a module's dependencies.
  • Assign its external value.

THE END

BY Matthew Phillips