The evolution of JavascriptDéjà addict à YUI 2 (YAHOO User Interface, version 2.x), j'ai enfin pris le temps de me pencher sur la version 3 de cette librairie Javascript. Je n'ai pas pu migrer immédiatement tous mes scripts car la librairie a été refondue en profondeur et n'a quasiment plus rien à voir avec la version 2. J'ai même cru à un plagiat de jQuery, ce qui a encore plus retardé la lecture de sa doc.

Le premier truc qui choque dans YUI3, c'est son concept de sandbox. Tout code javascript est conditionné dans des blocs hermétiques dans lesquels on aura chargé tel ou tel module. Ainsi, plusieurs développeurs peuvent travailler sur des scripts différents dans une même page sans avoir d'incidence l'un sur l'autre, chacun des codes étant parfaitement isolé.

Dans cette sandbox, on demande donc quels modules on veut utiliser. Et ça, c'est génial ! Les modules sont chargés dynamiquement selon les besoins. Dans ma page, je n'ai en fait chargé que la base de YUI3 : un fichier javascript de 7Ko. Et quand mon code sera exécuté, les modules que j'aurais demandé ne seront chargés que quand on en aura besoin.

Voyons ces deux choses en exemple :

  1. YUI().use('node', function(Y)
  2. {
  3. alert(Y.all('p').size()); // Affiche le nombre d'éléments p dans la page
  4. });

La sandbox, c'est le bloc ci dessus. Notre code est une fonction passée en argument de la méthode use() de l'objet YUI() auquel on aura préalablement indiqué quel module utiliser. Ici, on demande à utiliser le module "node". Il est alors disponible dans notre fonction à travers la variable Y. Si j'avais tenté d'utiliser Y.io(), qui est le module consacré aux requêtes AJAX, je me prendrais une erreur indiquant que Y.io n'existe pas. Il me faut donc demander son chargement au préalable :

  1. YUI().use('node', 'io', function(Y)
  2. {
  3. Y.io(
  4. '/index.php',
  5. {
  6. on: {
  7. {
  8. success: function(id, data)
  9. {
  10. // Insère le contenu de la réponse dans la première balise p trouvé dans la page
  11. Y.one('p').set('innerHTML', data.responseText);
  12. }
  13. }
  14. }
  15. }
  16. );
  17. });

Les fichiers node.js et io.js sont donc chargés depuis les serveurs de Yahoo dès que je les demande, et mon code est exécuté dès qu'ils sont disponibles.

Ça devient encore plus intéressant quand on écrit nos propres modules ou ceux de YUI2 qui restent compatible avec YUI3. Le constructeur de YUI prends en paramètre un objet de configuration dans lequel on peut indiquer les accès aux différents modules. Dans cette configuration, on peut tout d'abord indiquer des chemins pour les modules YUI3 si on préfère héberger nous même la librairie pour ne pas être tributaire de Yahoo. Mais on peut aussi définir des groupes de modules en indiquant leur emplacement et les dépendances. Prenons un exemple concret :

Ceci est une classe perso que je nomme alti_exemple et qui est accessible par le namespace Y.alti.exemple. Elle sera hébergée évidemment chez moi dans un fichier /js/alti.exemple.js contenant le code suivant :

  1. YUI().add('alti_exemple', function(Y)
  2. {
  3. Y.namespace('alti');
  4. Y.alti.exemple = function()
  5. {
  6. this.init();
  7. };
  8. Y.alti.exemple.prototype = {
  9. init: function()
  10. {
  11. // Du code qui utilise Y.node et Y.event
  12. }
  13. };
  14. }, '1.0', {
  15. requires: ['node', 'event']
  16. };

Dans ma page HTML, je vais renseigner une variable globale YUI_config qui sera la config par défaut de mes sandbox, puis après, je chargerais YUI. Dans cette config, j'ajoute les modules de yui2 nécessaire à l'utilisation de YAHOO.widget.Editor, l'éditeur de texte riche de YUI2 et mon module alti_exemple :

  1. <!DOCTYPE html>
  2. <title>Page Title</title>
  3. <script type="text/javascript" charset="utf-8">
  4. //<![CDATA[
  5. YUI_config = {
  6. groups: {
  7. yui2: {
  8. combine: true,
  9. base: 'http://yui.yahooapis.com/2.8.1/build/',
  10. comboBase: 'http://yui.yahooapis.com/combo?',
  11. root: '2.8.1/build/',
  12. modules: {
  13. yui2_yde: {
  14. path: 'yahoo-dom-event/yahoo-dom-event.js'
  15. },
  16. yui2_element: {
  17. path: 'element/element.js'
  18. },
  19. yui2_container: {
  20. path: 'container/container.js'
  21. },
  22. yui2_menu: {
  23. path: 'menu/menu.js'
  24. },
  25. yui2_button: {
  26. path: 'button/button.js'
  27. },
  28. yui2_editor: {
  29. path: 'editor/editor.js',
  30. requires: ['yui2_yde', 'yui2_element', 'yui2_container', 'yui2_menu', 'yui2_button']
  31. }
  32. }
  33. },
  34. alti: {
  35. combine: false,
  36. base: '/',
  37. root: 'js/',
  38. modules: {
  39. 'alti_exemple': {
  40. path: 'alti.exemple.js',
  41. requires: ['node', 'event']
  42. }
  43. }
  44. }
  45. }
  46. };
  47. //]]>
  48. </script>
  49. <script type="text/javascript" src="http://yui.yahooapis.com/combo?3.1.1/build/yui/yui-min.js" ></script>
  50. </head>
  51.  
  52. </body>
  53. </html>

Je peux maintenant utiliser ma classe, et les modules de YUI2 sans me demander si les fichiers ont bien été chargés au préalable :

  1. YUI().use('alti_exemple', function(Y)
  2. {
  3. var exemple = new Y.alti.exemple();
  4. });
  5.  
  6. YUI().use('yui2_editor', function(Y)
  7. {
  8. var editor = new YAHOO.widget.Editor(
  9. 'editor'
  10. );
  11. });
  12.  
  13. // Mixons les deux et utilisons node aussi !
  14. YUI().use('node', 'alti_exemple', 'yui2_editor', function(Y)
  15. {
  16. var exemple = new Y.alti.exemple();
  17. var editor = new YAHOO.widget.Editor(
  18. Y.one('textarea')
  19. );
  20. });

Alors, c'est pas pratique tout ça ?