I. Avant-propos

On m'a posé une question intéressante sur Ender (IRC : #enderjs sur Freenode) et tandis que j'y répondais, il m'est apparu que le sujet était un moyen idéal pour expliquer comment Ender regroupe plusieurs bibliothèques. Voici donc cette explication !

La question originale était :

« Lorsqu'un navigateur visite tout d'abord ma page, il se sert de Bonzo (une bibliothèque de manipulation du DOM) comme d'une bibliothèque autonome, mais à son retour sur la page, il se sert également de Qwery (un moteur de sélection), de Bean (un gestionnaire d'événements) et de quelques autres modules dans un package Ender. Puis-je intégrer Bonzo dans le package Ender pour les visiteurs assidus ? »

II. Présentation de Ender

Prenons un peu de recul et commençons avec quelques bases. La façon que j'utilise généralement pour expliquer aux gens ce qu'est EnderEnder est de le diviser en deux choses différentes :

  1. C'est un outil de construction pour le regroupement des bibliothèques JavaScript dans un seul fichier. Le fichier résultant constitue un nouveau "framework" basé sur le modèle de collection d'éléments du DOM de jQuery : $('selector').method(). Les bibliothèques constitutives fournissent les fonctionnalités des méthodes et peuvent également fournir la fonctionnalité du moteur de sélection ;
  2. C'est un écosystème de bibliothèques JavaScript. Ender favorise une petite collection de bibliothèques comme base, appelée le Jeesh, qui ensemble fournissent une grande partie des fonctionnalités normalement requises par un framework JavaScript, mais il y a beaucoup plus de bibliothèques compatibles avec Ender qui ajoutent des fonctionnalités supplémentaires. Beaucoup de bibliothèques disponibles pour Ender sont également utilisables en dehors de Ender en tant que bibliothèques autonomes.

Le Jeesh se compose des bibliothèques suivantes, chacune d'elles fonctionne également comme une bibliothèque autonome :

  • domReadydomReady : il détecte que le DOM est prêt pour la manipulation. Il fournit les méthodes $.domReady(callback) et $.ready(callback) ;
  • QweryQwery : un petit moteur rapide compatible avec les sélecteurs CSS3. Il effectue le travail de recherche d'éléments du DOM lorsque vous appelez $('selector') et fournit également les méthodes $(elements).find('selector'), $(elements).and(elements) et $(elements).is('selector') ;
  • BonzoBonzo : une bibliothèque de manipulation du DOM offrant, parmi les plus couramment utilisées, des méthodes telles que $(éléments).css('propriété', 'valeur'), $(elements).empty(), $(éléments).after(elements||html) et bien plus encore ;
  • BeanBean : un gestionnaire d'événements. Il ajoute des méthodes du type de celles de jQuery comme $(éléments).bind('événement', callback) et autres.

Le Jeesh vous donne les caractéristiques de ces quatre bibliothèques livrées dans un package de seulement 11,7 ko minimisé et gzippé.

III. Les bases : Bonzo

Commencer par Bonzo est une excellente façon d'appréhender Ender parce qu'il est utile rien qu'en lui-même. Incluons-le dans une page et faisons quelques manipulations du DOM vraiment simples.

 
Sélectionnez
<!DOCTYPE HTML>
<html lang="en-us">
<head>
 <meta http-equiv="Content-type" content="text/html; charset=utf-8">
 <title>Exemple 1</title>
</head>
<body>
 <script src="bonzo.js"></script>
 <script id="scr">
   // le contenu de ce script
   var scr = document.getElementById('scr').innerHTML

   // créer une balise <pre></pre>
   var pre = bonzo.create('<pre>')

   // remplir le texte, l'ajouter au body et lui donner des styles
   bonzo(pre)
     .text(scr)
     .css({
       fontWeight: 'bold',
       border: 'solid 1px red',
       margin: 10,
       padding: 10
     })
     .appendTo(document.body);

 </script>
</body>
</html>

Vous pouvez exécuter l'exemple 1, également disponible dans mon dépôt GitHub pour cet article.

Cela doit être relativement familier à un utilisateur de jQuery. Vous pouvez voir que Bonzo fournit certaines fonctionnalités importantes dont vous avez besoin pour modifier le DOM.

IV. Bonzo dans Ender

Nous allons voir ce qui se passe lorsque nous utilisons un simple package Ender qui inclut Bonzo. Nous allons également inclure Qwery donc nous pouvons outrepasser les interférences de document.getElementById(), et nous allons aussi utiliser Bean pour démontrer comment les bibliothèques peuvent s'assembler habilement.

Cela se fait en ligne de commande avec : ender build qwery bean bonzo. Un fichier nommé ender.js est créé. Il peut être chargé sur une page HTML appropriée.

Notre script devient :

 
Sélectionnez
$('<pre>')
 .text($('#scr').text())
 .css({
   fontWeight: 'bold',
   border: 'solid 1px red',
   margin: 10,
   padding: 10
 })
 .bind('click', function () {
   alert('Clic clac');
 })
 .appendTo('body');

Vous pouvez exécuter l'exemple 2, également disponible dans mon dépôt GitHub pour cet article.

Bonzo effectue la plupart du travail ici mais il est bien emmitouflé dans l'objet $ (aussi disponible sous la syntaxe ender). L'exemple précédent peut être résumé comme suit :

  • Bonzo.Create() est maintenant opérationnel lorsque le HTML est passé à $() ;
  • Qwery effectue le travail lorsque $() est appelée avec autre chose, dans ce cas, $('#scr') est utilisé comme un sélecteur pour l'élément script ;
  • Nous utilisons la variante sans argument de bonzo.text() pour aller chercher le innerHTML de l'élément script ;
  • Bean intervient avec l'appel de .bind(), mais le point important est qu'il est intégré dans notre chaîne d'appel même si c'est une bibliothèque distincte. C'est là que la magie de regroupement de Ender opère ;
  • bonzo.appendTo() prend l'argument du sélecteur qui est à son tour transmis à Qwery pour extraire l'élément sélectionné du DOM (document.body).

Chose également importante que nous n'avons pas encore démontrée, c'est que nous pouvons faire tout cela sur plusieurs éléments de la même collection. La première ligne peut être modifiée en :

 
Sélectionnez
$('<pre></pre><pre></pre>')

Et nous nous retrouverions avec deux blocs, tous deux en réponse à l'événement click.

V. Suppression de Bonzo

Il est possible de sortir Bonzo du package Ender et de l'y remettre manuellement par après. Exactement comme nous avions l'habitude de faire avec nos jouets lorsque nous étions enfants (ou il n'y a que moi ?) !

Tout d'abord, notre package Ender est maintenant créé avec : ender build qwery bean (ou bien nous pouvons exécuter ender remove bonzo pour supprimer Bonzo du fichier ender.js de l'exemple précédent). Le nouveau fichier ender.js contiendra la puissance du moteur de sélecteur de Qwery et la gestion de l'événement de Bean, mais pas grand-chose d'autre.

Bonzo peut être chargé séparément, mais nous avons besoin d'une colle spéciale pour ce faire. Dans le langage de Ender, cette colle est appelée un Ender Bridge.

VI. Ender Bridge

Ender suit le modèle de base de CommonJS Module. Il met en place un registre de module simple et donne à chaque module un objet module.exports ainsi qu'une méthode require() qui peut être utilisée pour aller chercher les autres modules dans le package. Elle utilise également une méthode provide('name', module.exports) pour insérer les exportations dans le registre avec le nom de votre module. Les détails exacts ne sont pas importants ici et j'expliquerai comment vous pouvez créer votre propre module Ender dans un prochain article. Pour le moment, nous avons simplement besoin d'une compréhension basique du système de registre des modules.

À l'aide de notre package contenant Qwery, Bean et Bonzo, le fichier ressemble à ceci :

 
Sélectionnez
|========================================|
| Initialisation Ender et registre de    |
| module. Nous pouvons appeler ceci la   |
| bibliothèque cliente.                  |
|========================================|
| 'module.exports' setup                 |
|----------------------------------------|
| Qwery source                           |
|----------------------------------------|
| provide('qwery', module.exports)       |
|----------------------------------------|
| Qwery bridge                           |
==========================================
| 'module.exports' setup                 |
|----------------------------------------|
| Bean source                            |
|----------------------------------------|
| provide('bean', module.exports)        |
|----------------------------------------|
| Bean bridge                            |
==========================================
| 'module.exports' setup                 |
|----------------------------------------|
| Bonzo source                           |
|----------------------------------------|
| provide('bonzo', module.exports)       |
|----------------------------------------|
| Bonzo bridge                           |
==========================================


Pour être une bibliothèque utile, le code doit être capable d'adhérer au modèle CommonJS Module si un objet module.exports ou un objet exports existe. De nombreuses bibliothèques font déjà cela donc elles peuvent fonctionner ensemble dans le navigateur et dans un environnement de CommonJS comme Node. Considérons Underscore.js par exemple, il détecte l'existence d'exportsexports et s'insère sur cet objet s'il existe, sinon il s'insère dans l'objet global (c'est-à-dire l'objet window). C'est ainsi que les bibliothèques compatibles avec le package Ender peuvent aussi servir de bibliothèques autonomes.

Ainsi, à sauter sur les complexités, nos bibliothèques sont dès lors enregistrées au sein de Ender et cela nous mène au Bridge. Techniquement, le Bridge est simplement un bout de code arbitraire exprimant que les bibliothèques compatibles avec Ender sont autorisées à fournir l'outil Ender CLI ; cela pourrait être n'importe quoi. L'intention, cependant, est de l'utiliser comme une colle pour lier la bibliothèque au noyau de l'objet ender / $. Un Bridge n'est pas nécessaire et peut être omis. Dans ce cas, tout ce qui est trouvé sur le module.exports est automatiquement lié à l'objet ender / $. Underscore.js n'a pas besoin d'un Bridge car il est conforme au modèle standard CommonJS et ses méthodes sont des utilitaires qui, logiquement, appartiennent à $. Par exemple, $.each(list, callback). Si un module doit fonctionner sur les collections $('selector'), alors il a besoin d'une liaison spéciale pour ses méthodes. De nombreux modules exigent aussi des liaisons très complexes afin qu'elles puissent fonctionner correctement à l'intérieur de l'environnement Ender.

Bonzo est un des Bridge les plus complexes que vous trouverez dans l'environnement Ender, donc nous n'irons pas plus loin sur ce sujet. Si vous êtes intéressés à en savoir davantage, un simple lien avec quelques fonctionnalités intéressantes peut être trouvé du côté de MorpheusMorpheus, un framework d'animation pour Ender. MorpheusMorpheus ajoute une méthode $.tween(), une méthode $('selector').animate() et des méthodes supplémentaires.

La forme la plus simple de Bridge est celle qui intègre les méthodes module.exports dans un nouvel espace de noms. Examinons Moment.jsMoment.js, la bibliothèque populaire pour gérer les dates. Lorsqu'elle est utilisée dans un environnement CommonJS, elle ajoute toutes ses méthodes à l'objet module.exports. Sans un Bridge, lorsqu'elle est ajoutée à un package Ender, vous obtenez les méthodes $.utc(), $.unix(), $.add(), $.subtract() et d'autres qui n'ont pas des noms très significatifs en dehors de Moment.jsMoment.js. Elles sont également susceptibles d'entrer en conflit avec d'autres bibliothèques que vous pouvez ajouter à votre package Ender. La solution logique est de les nommer $.moment.utc() etc., puis vous pouvez également utiliser la fonction principale exportée $.moment (Date|String|Nombre). Pour se faire, le Bridge de Moment.jsMoment.js ressemble à ceci :

 
Sélectionnez
$.ender({ moment: require('moment') })


La méthode $.ender() est la manière pour un Bridge d'ajouter des méthodes à l'objet global ender / $. Elle prend un argument facultatif de type booléen pour indiquer si les méthodes peuvent fonctionner sur les collections d'éléments du DOM, c'est-à-dire $('selector').method().

VII. Bonzo à part

Retour à ce que nous essayons d'atteindre : nous allons charger Bonzo comme une bibliothèque autonome et nous voulons l'intégrer dans un package Ender dans le navigateur. Il y a deux choses importantes que nous devons faire pour y parvenir : (1) charger le Bridge de Bonzo, donc il peut intégrer Bonzo dans Ender et (2) sensibiliser Ender à Bonzo. Un require('bonzo') fera l'affaire parce que c'est comme ça que le Bridge fait le lien avec Bonzo.

Tout d'abord faisons cela facilement. Avec un package Ender qui contient uniquement Qwery et Bean et le Bridge de Bonzo dans un fichier distinct nommé bonzo-ender-bridge.js. Nous pouvons faire ce qui suit :

 
Sélectionnez
<!-- l'ordre d'importation n'a pas d'importance -->
<script src="ender.js"></script>
<script src="bonzo.js"></script>
<script>
 provide('bonzo', bonzo)
</script>
<script src="bonzo-ender-bridge.js"></script>


Si vous regardez la structure des fichiers Ender fournis ci-dessus, vous verrez que nous la répliquons avec nos balises script mais en remplaçant provide('bonzo', module.exports) par provide('bonzo', bonzo) car Bonzo a détecté qu'il ne fonctionne pas à l'intérieur d'un environnement CommonJS avec l'objet module.exports disponible. Au lieu de cela, il s'est lui-même attaché à l'objet global (window). La méthode provide() tant que la méthode require() sont disponibles sur l'objet global et peuvent être utilisées en dehors de Ender (par exemple, pour extraire les Bean sur un package intégré, vous pourriez simplement écrire var bean = require('bean')).

Maintenant, nous pouvons continuer d'utiliser exactement le même script comme dans notre exemple de package Ender entièrement intégré :

 
Sélectionnez
$('<pre>')
 .text($('#scr').text())
 .css({
   fontWeight: 'bold',
   border: 'solid 1px red',
   margin: 10,
   padding: 10
 })
 .bind('click', function () {
   alert('Clic clac');
 })
 .appendTo('body');

Vous pouvez exécuter l'exemple 3, également disponible dans mon dépôt GitHub pour cet article.

VIII. Réduire les balises script

Le principal problème avec le dernier exemple est que nous avons trois balises script dans notre page avec des fichiers chargés depuis notre serveur (de manière synchrone). Nous pouvons réduire deux d'entre eux, et si bonzo.js est déjà mis en cache dans le navigateur, il ne chargera plus qu'un seul script.

Nous pourrions atteindre ce but en piratant le fichier ender.js afin d'y inclure le code nécessaire, ou nous pourrions créer notre propre package Ender qui contient notre code afin qu'il reste présent même après que l'outil CLI Ender est en action.

Tout d'abord, nous faisons un nouveau répertoire pour contenir notre package. Nous allons inclure le Bridge de Bonzo dans un fichier distinct et nous allons également créer un fichier pour déclarer la méthode provide(). Enfin, un fichier de base package.json pointe vers notre fichier provide() en tant que source (main) du package et le Bridge de Bonzo pointe vers le fichier de notre Bridge ("ender") :

 
Sélectionnez
{
 "name": "fake-bonzo",
 "version": "0.0.0",
 "description": "Fake Bonzo",
 "main": "main.js",
 "ender": "bonzo-ender-bridge.js"
}


Nous pointons alors le Ender CLI vers ce répertoire : ender build qwery bean ./fake-bonzo/ (ou nous pourrions exécuter ender add ./fake-bonzo/ pour l'ajouter au fichier ender.js créé dans l'exemple ci-dessus).

La page complète ressemble maintenant à ceci :

 
Sélectionnez
<!DOCTYPE HTML>
<html lang="en-us">
<head>
 <meta http-equiv="Content-type" content="text/html; charset=utf-8">
 <title>Exemple 4</title>
</head>
<body>
 <script src="bonzo.js"></script>
 <script src="ender.js"></script>
 <script id="scr">
   $('<pre>')
     .text($('#scr').text())
     .css({
       fontWeight: 'bold',
       border: 'solid 1px red',
       margin: 10,
       padding: 10
     })
     .bind('click', function () {
       alert('Clic clac');
     })
     .appendTo('body');

 </script>
</body>
</html>

Vous pouvez exécuter l'exemple 4, également disponible dans mon dépôt GitHub pour cet article.

IX. Conclusion

J'espère que cela a aidé à démystifier la façon dont procède Ender pour regrouper les bibliothèques ensemble. Il n'y a rien de magique. Si vous voulez creuser un peu plus alors un bon point de départ serait d'examiner la bibliothèque cliente qui apparaît en haut de chaque package de Ender. C'est assez simple et assez court.

X. Remerciements

Cet article a été publié avec l'aimable autorisation de Rod Vagg. L'article original peut être lu sur le site DailyJSDailyJS : How Ender Bundles Libraries for the BrowserHow Ender Bundles Libraries for the Browser.
Je remercie également ClaudeLELOUP pour sa relecture attentive et assidue.