Clientseitige Abhängigkeiten in JavaScript verwalten



Clientseitige Abhängigkeiten in JavaScript verwalten

0 0


fedeps

Presentation on managing frontend dependencies

On Github boundaryfunctions / fedeps

Clientseitige Abhängigkeiten in JavaScript verwalten

Von Marc Löhe / @bfncs

Struktur

Status Quo CommonJS Module AMD (Require.js) Browserify ES6 Module

Status Quo

<script src="jQuery.js"></script>
<script src="myPreciousPlugin.js"></script>
<script src="anotherPlugin.js"></script>
<!-- etc... -->
<script src="oneMoreplugin.js"></script>
<script src="dom.js"></script>

Status Quo

(function () {
	var $ = this.jQuery;

	this.myExample = function () {
		$.doSomething();
	};
}());
Beispiel von requirejs.org

Status Quo

  • Module werden definiert durch eine Factory-Funktion zur Abkopplung vom restlichen Code.
  • Abhängigkeiten werden als globale Variablen referenziert, die bereits vorher irgendwie geladen sein müssen.
  • Die Abhängigkeiten sind schwach defininiert: der Entwickler muss selber wissen, wann er welchen Bestandteil wie lädt.

In größerem Projekten

Spaghetti!

Maintainability

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.” John F. Woods

Wie kann man JavaScript-Code im Browser effizient und übersichtlich modularisieren?

Common JS / Node.js Module

circle.js

var PI = Math.PI;

exports.area = function (r) {
return PI * r * r;
};

app.js

var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is '
+ circle.area(4));
Beispiel aus der Node.js-Dokumentation

Common JS im Browser

Das in Node verwendete Modulformat wurde von der CommonJS-Gruppe standardisiert und lässt sich prinzipiell so auch im Browser implementieren.

Common JS im Browser

Der Standard berücksichtigt aber leider nicht die besonderen technischen Bedingungen im Browser:

  • Module müssen über das Netzwerk geladen werden
  • Das Laden der Module findet daher grundsätzlich asynchron statt.
  • Es gibt keine browserübergreifende API, jede Webseite/-anwendung muss ihren eigenen Lademechanismus mitbringen.

Asynchronous module definition (AMD)

Mit dem AMD-Standard wurde versucht möglichst viele Vorteile von CJS-Modulen in die Welt der Browser zu bringen, in dem es die technischen Eigenheiten der Browser-Infrastruktur berücksichtigt.

AMD: API

  • define um Module zu definieren
  • require um Abhängigkeiten zu laden

AMD: define

define(
  module_id /*optional*/,
  [dependencies] /*optional*/,
  definition function /*instantiate the module*/
);
					
Die module_id wird i.d.R. automatisch aus dem Pfad generiert und nicht explizit im Quelltext definiert (DRY).

AMD: define

define(
  'myModule',
  ['foo', 'bar'],
  function (foo, bar) {
    return myModule = {
	  doStuff: function() {
	    console.log('Yay! Stuff');
	  }
    };
});
					

AMD: require

require(
  ['foo', 'bar'],
  function ( foo, bar ) {
    // Modules foo and bar are now loaded and ready to be used
    foo.doSomething();
});
					

AMD: Implementation

Der Standard AMD wird durch mehrere verschiedene JS-Loader-Bibliotheken implementiert:

require.js

index.html

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

js/app.js

requirejs.config({
  baseUrl: 'js/lib',
  paths: {
    app: '../app'
}});

requirejs(
  ['jquery', 'canvas', 'app/sub'],
  function   ($, canvas, sub) {
    // All modules are loaded and can be used now.
});
					

Pro

  • Funktioniert asynchron im Browser
  • Klare Definition der Abhängigkeiten
  • Ermöglicht ein performantes asynchrones Nachladen
  • Mehrere Module können in einer Datei definiert werden (daher ein CJS-Transportformat)
  • Ermöglicht einfachen Austauch von Modulen durch Mockups für Unit-Testing

Contra

  • Viele HTTP-Requests, typischerweise ein File pro Library: unperformant
  • Viel Boilerplate-Code, insbesondere wenn mehrere Loader-Formate unterstützt werden sollen (jQuery+CJS+ASM)
  • Asynchrones Laden ist im Prinzip eine tolle Sache, aber schadet hier oft mehr, als es nutzt

Browserify

Browserify lets you require('modules') in the browser by bundling up all of your dependencies.

Browserify: Installieren

npm install -g browserify

Ggfs. das Modul für das Buildsystem der Wahl mitinstallieren: grunt, gulp, etc.

Browserify: Beispiel

main.js

var unique = require('uniq');
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
console.log(unique(data));
                    

Modul mit installieren und kompilieren

npm install uniq
browserify main.js -o bundle.js
                    

index.html

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

ES6 modules

mymodule.js

export class q {
    constructor() {
        console.log('this is an es6 class!');
    }
}
                    

mymodule.js

<script>
    System.import('mymodule').then(function(m) {
        new m.q();
    });
</script>
                    

Für Unentschlossene

SystemJS

Universal dynamic module loader - loads ES6 modules, AMD, CommonJS and global scripts in the browser and NodeJS. Works with both Traceur and Babel. https://github.com/systemjs/systemjs

Jetzt ihr:

  • Welche Techniken benutzt ihr?
  • Welche Erfahrungen habt ihr gemacht?
  • Was funktioniert besonders gut/schlecht?
  • Vermisst ihr etwas?

Links

Bilder:

Vielen Dank!

… und hier in Ruhe nachlesen:https://github.com/boundaryfunctions/fedeps

Clientseitige Abhängigkeiten in JavaScript verwalten Von Marc Löhe / @bfncs