Javascript patterns

&

Module loaders

Text

Obbiettivi

  • Capire cos'è un pattern javascript e sapere quali sono quelli più utilizzati
     
  • Primo approfondimento sul patterne CommonJS
     
  • Cos'è un bundler e come usare Browserify

Il problema

Javascript non offre un metodo per gestire le dipendenze nativamente. Less ha @import, Php ha require(), ecc...

Questo crea non pochi problemi all'organizzazione dei progetti, soprattutto quelli di medie grandi dimensioni.

Javascript Patterns

  1. IIFE (Immediately Invoked Function Expression)
     
  2. CommonJS (Node)
     
  3. AMD (Require.js)

LA SOLUZIONE

Module

Un modulo è un pezzo di codice isolato e agnostico.

Un modulo può avere dipendenze, ma devono essere esplicitate.

Un modulo deve restituire un risultato.

IIFE

(Immediately Invoked Function Expression)

Una soluzione a metà

(function(root) {
  var calculator = {
    sum: function(a, b) { 
      return a + b;
    }
  };
  root.calculator = calculator;
})(this);
console.log(calculator.sum(1, 2)); // => 3

calculator.js

app.js

All'interno delle funzioni abbiamo privacy, e possiamo esporre nello scope globale solo quello che ci serve

Con questo metodo però non creiamo nessuna alberatura esplicita delle nostre dipendenze. 

Ci tocca farcelo a noi, con conseguente rischio di

TypeError: undefined is not a function

CommonJS

(Node.js [CommonJs è un comitato spontaneo]) link

module.exports = {
  sum: function(a, b) { return a + b; }
};
var calculator = require('./calculator');

console.log(calculator.sum(1, 2));

calculator.js

app.js

  • Molto più elegante di IIFE
  • Non vi è modo di accedere al file calculator.js dall'esterno
  • Viene esportato SOLO il contenuto di module.exports

Require() legge il file specificato e ritorna il contenuto dell'oggetto module.exports senza eseguirlo.

 

 

require('jquery'); // node_moules
require('./jquery'); // cartella attuale
require('../vendors/jquery'); // cartella vendors

La chiamata Require è sincrona. Per questo motivo per funzionare in un browser, il comitato CommonJS ha pensato ad un'alternativa: il pattern asincrono AMD.

AMD

(Asynchronous Module Definition)

define("calculator", function() {
  return {
    sum: function(a, b) { return a + b; }
  };
});
define("app", ["calculator"], function(calculator) {
  console.log(calculator.sum(1, 2)); // => 3
});

calculator.js

app.js

  • define() definisce sia il modulo che le dipendenze necessarie per il funzionamento dello stesso.
  • Le dipendenze vengono caricate in modo asincrono, e poi vengono passate alla callback sotto forma di parametro.

Se il modulo prevede più di una dipendenza occorre preoccuparsi di rispettare l'ordine dei parametri.

 

Questa soluzione è meno elegante rispetto a Node.js, ma è l'unica via percorribile all'interno di un browser.

(per caricare progressivamente solo gli script che ci servono e far avanzare la nostra app senza dover aspettare il caricamento completo di un unico monolite) 

 

Nella pratica però, su progetti medio/grandi vi è un overhead http che compromette le prestazioni.

 

Require.js (popolare implementazione del pattern AMD)

propone di creare preventivamente un bundle. link

ECMAScript 6

(Javascript 2015) - link

var calculator = function() {
  return {
    sum: function(a, b) { return a + b; }
  };
};

export default calculator;
import calculator from 'calculator';

console.log(calculator.sum(1, 2)); // => 3

calculator.js

app.js

L'obbiettivo di ES6 è di creare un module loader pattern che sia elegante come CommonJs e che abbia il supporto per il caricamento asincrono delle dipendenze come AMD

Browserify ci permette di utilizzare i moduli del pattern CommonJs (node) all'interno del un browser

Browserify fa un'analisi dell'alberatura delle dipendenze partendo da un entry-point.

Ordina le dipendenze del nostro progetto e si occupa di richiamarle (eventualmente ricorsivamente) quando necessario.

Cosa fa

I transformers

Un ulteriore vantaggio di usare un bundler è la possibilità di utilizzare dei transformers, come ad esempio coffeescript.

module.exports = (n) -> n * 111

baz.coffee

module.exports = require('./baz.coffee')(5)

bar.js

Oppure è possibile includere il transformer Babelify per poter utilizzare la sintassi del nuovo ES6.

Javascript Module Loaders

By giulico

Javascript Module Loaders

  • 889