Require.js me fait mal au cerveau. Quelques questions fondamentales sur la façon dont il charge les scripts / modules

Supposons que ceci soit mon config.js ou main.js:

require.config({ // paths are analogous to old-school <script> tags, in order to reference js scripts paths: { jquery: "libs/jquery-1.7.2.min", underscore: "libs/underscore-min", backbone: "libs/backbone-min", jquerymobile: "libs/jquery.mobile-1.1.0.min", jquerymobilerouter: "libs/jquery.mobile.router.min" }, // configure dependencies and export value aliases for old-school js scripts shim: { jquery: ["require"], underscore: { deps: ["jquery"], exports: "_" }, backbone: { deps: ["underscore", "jquery"], exports: "Backbone" }, jquerymobilerouter: ["jquery", "backbone", "underscore"], jquerymobile: ["jquery", "jquerymobilerouter", "backbone", "underscore"] } }); require(["jquery", "backbone", "underscore", "app/app.min", "jquerymobilerouter", "jquerymobile"], function ($, Backbone, _, App) { console.log($); console.log(Backbone); console.log(_); $("body").fadeIn(function () { App.init(); }); }); 
  1. Si je comprends bien, l'option de configuration des paths vous permet de faire référence aux scripts, a-la la <script> dans HTML. En supposant que c'est le cas, ai-je encore besoin de scripts alias comme jQuery avec un $ ou un trait de soulignement avec un _ dans mon état actuel requis ci-dessous? Il semble étrange que je devienne, étant donné que si vous faites référence à jQuery avec une étiquette <script> standard, $ peut être utilisé dans votre script automatiquement. Ne devrait-il pas être le même en utilisant les paths ?

  2. Je suis nouveau dans l'option shim config, ce qui, je l'ai compris, a remplacé l' order! obsolète order! brancher. Qu'est-ce que la propriété des exports FAIT effectivement? Il ne semble pas créer un alias pour un script; Par exemple, si je définis les exports pour le trait de soulignement sur "whatever" , puis essayez de console.log(whatever) , il est indéfini. Alors, quel est le but?

  3. Comment les scripts comme jQuery devraient-ils être utilisés correctement "globalement?" C'est-à-dire, quel est le bon moyen d'utiliser l'alias $ dans mon module App.js ou tout autre module dans mon dossier "application"? Dois-je exiger jQuery dans chaque module individuel et alias $ chaque fois? Ou est-ce que je l'ai fait ici de la bonne façon?

J'apprécierais beaucoup d'autres critiques de ce script particulier; La documentation de Require.js, à mon avis, laisse beaucoup à désirer; Les choses que j'aimerais en savoir plus au sujet de semblent se glisser et me laisser me gratter la tête.

  1. Les chemins indiquent require.js où chercher quand vous avez besoin de cette dépendance.

    Par exemple, j'ai configuré comme ceci:

     "paths": { "jquery": "require_jquery" }, "shim": { "jquery-cookie" : ["jquery"], "bootstrap-tab" : ["jquery"], "bootstrap-modal": ["jquery"], "bootstrap-alert": ["jquery"] }, 

    Cela signifie que chaque fois dans un module que je fais

     define( ['jquery'] 

    Requirejs charge le fichier require_jquery partir du chemin principal au lieu d'essayer de charger jquery.js. Dans votre cas, il chargeait le fichier source jQuery, qui serait alors disponible dans le monde entier. Personnellement, je n'aime pas cette approche et, pour cette raison, dans le fichier require_jquery.js que je fais:

     define( ["jquery_1.7.2"], function() { // Raw jQuery does not return anything, so return it explicitly here. return jQuery.noConflict( true ); } ); 

    Ce qui signifie que jQuery sera défini uniquement dans mes modules. (C'est parce que j'écris des plugins WordPress et donc je peux inclure ma propre version de jQuery sans toucher la version externe)

  2. Les exportations (la lecture des docs doit simplement être le nom du module que vous utilisez pour que cela puisse être détecté si le chargement est passé correctement. Voici pourquoi s'il vous plaît, si vous souhaitez définir une exportation pour le trait de soulignement,

  3. JQuery devrait être global comme je l'ai expliqué, si vous l'importez simplement, le fichier est exécuté et jQuery est global

EDIT – pour répondre aux commentaires.

  1. Oui, je veux dire que vous devez exporter $ ou jQuery pour jQuery et _ pour backbone. D'après ce que j'ai reçu des docs, cela n'est nécessaire que dans certains cas de pointe et ne serait pas nécessaire pour les bibliothèques qui se déclarent dans l'espace de noms global comme jQuery.

    Je pense que requirejs a besoin d'eux quand il doit reculer de charger jQuery d'un CDN. Je pense que requiert d'abord d'essayer de charger jQuery à partir du CDN, puis vérifie qu'il a été chargé correctement en vérifiant que la variable "exporté" existe, et si elle ne le charge pas dans le système de fichiers local (si vous Avait configuré des repli, bien sûr). C'est quelque chose qui est nécessaire lorsque requirejs ne peut pas voir un 404 revenir.

  2. JQuery est mondialement disponible car il est déclaré mondial. Si vous chargez et exécutez simplement le script jQuery, vous finirez par deux globals, $ et jQuery (ou vous pouvez faire comme je l'ai fait et éviter cela). A l'intérieur de la fonction define() , vous pouvez être n'importe quoi jQuery pour être ce que vous voulez.

     define( [ 'jquery' ], function( jq ) { // jq is jquery inside this function. if you declared it // globally it will be also available as $ and jQuery } ); 

Juste pour éliminer toute confusion autour des exports , on suppose que toute bibliothèque de caisse attache une propriété au contexte global ( window ou root ), ou modifie une propriété globale déjà existante (par exemple, un plugin jQuery). Lorsque requireJS obtient la commande pour charger une dépendance réduite, elle examine le contexte global d'une propriété correspondant à la valeur des exports de cette configuration de shim, et si elle le trouve, le retourne comme valeur de ce module. S'il ne le trouve pas, il charge le script associé, attend qu'il s'exécute, puis trouve le symbole global et le renvoie.

Un fait important à retenir est que, à moins que la configuration shim contienne une valeur exports , aucune méthode d' init sur cette configuration ne sera PAS exécutée. Le chargeur de dépendance doit localiser une valeur pour le module (c'est ce que les exports spécifient) avant que ce module ne puisse être initialisé, c'est pourquoi la propriété est requise s'il existe un point d'inactivité pour ce module.

Mise à jour: je dois également souligner que si les appels du module en question define n'importe où, toute configuration de shim que vous avez pour ce module sera ignorée. Cela m'a effectivement causé des maux de tête parce que je voulais utiliser la configuration shim pour appeler la jQuery.noConflict(true) de jQuery jQuery.noConflict(true) pour désorganiser jQuery et garder la portée des modules qui en nécessitent mais ne parviennent pas à fonctionner . (Voir la mise à jour en bas pour obtenir des informations sur la façon de procéder facilement en utilisant la configuration de la carte au lieu de la configuration de shim.)

Mise à jour 2: une question récente sur le groupe google de requireJS m'a permis de me rendre compte que mon explication pourrait être un peu trompeuse, alors j'aimerais clarifier. RequireJS ne réutilisera qu'une dépendance réduite si elle a été chargée via requireJS au moins une fois . C'est-à-dire, si vous avez simplement une <script> sur la page d'hébergement (par exemple, le trait de soulignement), comme ceci:

 <script src='lib/underscore.js'></script> <script src='lib/require.js' data-main='main.js'></script> 

… et vous avez quelque chose comme ça dans votre configuration requireJS:

 paths: { 'underscore': 'lib/underscore' }, shim: { 'underscore': { exports: '_' } } 

Ensuite, la première fois que vous define(['underscore'], function (_) {}); Ou var _ = require('underscore'); , RequireJS va recharger la bibliothèque de soulignement plutôt que de réutiliser la window._ précédemment window._ , car, dans la mesure où requiertJS sait, vous n'avez jamais chargé le trait de soulignement avant. Bien sûr, il peut vérifier si _ est déjà défini sur la portée de la racine, mais il n'a aucun moyen de vérifier que le _ qui existe déjà est le même que celui défini dans la configuration de votre paths . Par exemple, le prototype et jquery s'attachent à la window.$ Par défaut, et si requireJS suppose que 'window. $' Est jQuery quand il est en fait un prototype, vous allez être en mauvaise situation.

Tout cela signifie que si vous mélangez et combinez des styles de chargement de script comme celui-ci, votre page se terminera par quelque chose comme ceci:

  <script src='lib/underscore.js'></script> <script src='lib/require.js' data-main='main.js'></script> <script src='lib/underscore.js'></script> 

Lorsque la seconde instance de soulignement est celle chargée par requireJS.

Fondamentalement, une bibliothèque doit être chargée via requireJS pour que requireJS en ait connaissance. Cependant, la prochaine fois que vous avez besoin du trait de soulignement, requireJS ira "hey", j'ai déjà chargé cela, alors merci de retourner la valeur des exports et ne vous inquiétez pas de charger un autre script. "

Cela signifie que vous avez deux options réelles. L'un est ce que je considère comme un anti-modèle: n'utilisez pas requireJS pour exprimer des dépendances pour les scripts globaux. C'est-à-dire, tant qu'une bibliothèque attache un contexte global au racine, vous pourrez y accéder, événement si cette dépendance n'est pas explicitement requise. Vous pouvez voir pourquoi il s'agit d'un anti-modèle – vous avez fondamentalement éliminé la plupart des avantages de l'utilisation d'un chargeur AMD (liste de dépendance explicite et portabilité).

L'autre meilleure option est d'utiliser requireJS pour tout charger, dans la mesure où la seule étiquette de script réelle que vous devez créer vous-même est celle qui nécessite initialement les besoins de JS. Vous pouvez utiliser des cales, mais 95% du temps, il n'est vraiment pas si difficile d'ajouter un wrapper AMD au script à la place. Cela pourrait prendre un peu plus de travail pour convertir toutes vos bibliothèques non-AMD pour être compatibles avec AMD, mais une fois que vous avez fait une ou deux, cela devient beaucoup plus facile – je peux prendre n'importe quel plugin générique jQuery et le convertir en un module AMD En moins d'une minute. Il ne s'agit généralement que d'ajouter

 define(['jquery'], function (jQuery) { 

Au sommet, et

  return jQuery; }); 

au fond. La raison pour laquelle j'ai 'jquery' cartographier jQuery plutôt que $ est que j'ai remarqué que la plupart des plugins ces jours-ci sont enveloppés dans une fermeture comme ceci:

 (function ($) { // plugin code here })(jQuery); 

Et c'est une bonne idée de faire attention à la portée prévue. Vous pouvez certainement mapper 'jquery' à $ directement, en supposant que le plugin ne s'attend pas à trouver jQuery au lieu de $ . C'est juste l'emballage AMD de base – les plus complexes tentent généralement de détecter quel type de chargeur est utilisé (commonJS vs AMD vs ordinals) et utiliser une méthode de chargement différente en fonction du résultat. Vous pouvez trouver des exemples de ceci très facilement avec quelques secondes sur google.

Mise à jour: la solution de contournement utilisée par jQuery.noConflict(true) avec RequireJS a fonctionné, mais elle nécessitait une très petite modification de la source jQuery, et j'ai découvert une façon beaucoup plus efficace d'accomplir la même chose sans modifier jQuery. Heureusement, James Burke, l'auteur de RequireJS, l'a ajouté à la documentation RequireJS: http://requirejs.org/docs/jquery.html#noconflictmap