45 votes

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

Supposons ceci est 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' paths option de configuration vous permet de scripts de référence, à la de la <script> balise dans le code HTML. En supposant que c'est le cas, dois-je encore besoin de faire un alias de scripts comme jQuery avec un $ ou un trait de soulignement avec un _ dans ma réelle exiger la déclaration ci-dessous? Il semble étrange que j'avais, étant donné que si vous faites référence à jQuery avec une norme <script> balise, $ peut être utilisé tout au long de votre script automatiquement. Ne devrait-elle pas être la même à l'aide de l' paths?

  2. Je suis nouveau sur le shim option de configuration, que je comprends a remplacé le obsolète order! plugin. Ce qui ne l' exports bien FAIRE réellement? Il ne semble pas créer un alias pour un script; par exemple, si j'ai mis l' exports pour le trait de soulignement pour "whatever", puis essayez d' console.log(whatever), c'est pas défini. Alors, quel est le point?

  3. Comment des scripts comme jQuery être correctement utilisé, à l'échelle mondiale?" C'est, ce est la bonne façon d'être en mesure d'utiliser l' $ alias dans mon App.js module, ou de tout autre module dans mon "app" dans le dossier? Dois-je exiger de jQuery à l'intérieur de chaque module individuel et alias $ à chaque fois? Ou est la façon dont je l'ai fait ici la bonne façon?

J'avais apprécie beaucoup tous les autres critiques de ce script particulier ainsi; la documentation de Require.js à mon avis, laisse beaucoup à désirer; des choses que je voudrais vraiment en savoir plus à propos semblent s'estomper et de me laisser me gratter la tête.

22voto

Nicola Peluchetti Points 38948
  1. Les chemins de dire require.js où chercher quand vous avez besoin que de dépendance.

    Par exemple, j'ai des choses 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, je ne

    define( ['jquery']
    

    requirejs charge le fichier require_jquery de la voie principale au lieu d'essayer de charger jquery.js. Dans votre cas, il serait de charger jQuery fichier source qui sera disponible dans le monde entier. Personnellement, je n'aime pas cette approche et pour cette raison, dans le require_jquery.js fichier 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éfinie qu'à l'intérieur de mes modules. (C'est parce que j'écris des plugins Wordpress et donc je peut comprendre ma propre version de jQuery sans toucher l'extérieur de la version)

  2. Les exportations (la lecture de la documentation doit tout simplement être le nom du module que vous utilisez, de sorte qu'il peut être détecté si le chargement est allé correctement. Ici est expliqué. Donc, si vous souhaitez définir un export pour souligner qu'il devrait être _

  3. jQuery doit être globale, comme je l'ai expliqué, si vous avez simplement importer le fichier est exécuté et jQuery est mondial

MODIFIER - pour répondre aux commentaires.

  1. oui, je veux dire que, vous devez exporter $ ou jQuery jQuery et _ pour la colonne vertébrale. De ce que j'ai reçu de la doc cela n'est nécessaire que dans certains cas limites et ne serait pas nécessaire pour les bibliothèques de se déclarer dans l'espace de noms global comme jQuery.

    Je pense que requirejs a besoin d'eux quand il a pour secours le chargement de jQuery à partir d'un CDN. je pense que requirejs essaie d'abord de charger jQuery à partir de la CAN, puis fait un contrôle afin de vérifier qu'il a été correctement chargé de vérifier que le "exporté" à la variable existe, et si ça ne marche pas il charge, il forme le système de fichiers local (si vous aviez configuré de base, bien sûr). C'est quelque chose qu'il est nécessaire lors de requirejs ne pouvez pas voir une erreur 404 revenir.

  2. jQuery est disponible dans le monde entier parce qu'il est déclaré mondiale. Si vous il suffit de les charger et d'exécuter le script jQuery, vous allez vous retrouver avec deux globals, $ et jQuery (ou vous pouvez faire comme moi et de les éviter). À l'intérieur de l' define() fonction, vous pouvez alias 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
    } );
    

22voto

Isochronous Points 390

Juste pour dissiper toute confusion autour de exports, il est supposé que toute la cale de la bibliothèque s'attache à une propriété pour le contexte global (window ou root), ou modifie un déjà existant mondiale de la propriété (par exemple, un plugin jQuery). Lorsque requireJS obtient la commande pour charger un calée dépendance, il examine le contexte global pour un bien correspondant à la exports de la valeur de la cale de config, et si il le trouve, il la retourne comme valeur de ce module. Si il ne le trouve pas, alors il charge le script associé, attend à ce qu'il exécute, puis trouve le symbole mondial et le renvoie.

Un fait important à retenir est que, à moins que la cale config contient un exports de la valeur, tout init méthode sur cette config ne sera PAS exécuté. La dépendance chargeur doit localiser une valeur pour le module (qui est ce que l' exports précise) avant que le module peut être initialisé, ce qui est pourquoi la propriété est requis s'il y a une cale init de ce module.

mise à jour: j'ai aussi besoin de rappeler que si le module en question exige define de n'importe où, cale config que vous avez pour ce module sera ignoré. En fait, cela m'a causé quelques maux de tête parce que je voulais utiliser la cale de config à l'appel de jQuery jQuery.noConflict(true) méthode de l'onu-globify jQuery et le garder à portée seulement les modules qui en ont besoin, mais j'ai fini par avoir à créer une jquery-noconflict.js fichier qui n'a, a 'jquery' pathed à l'jquery-noconflict.js fichier, de modifier notre copie locale de jQuery afin qu'il définit lui-même comme "jQuery 1.9.1' au lieu de simplement 'jQuery', et ont jquery-noconflict.js besoin d'jQuery 1.9.1 " au lieu de jQuery. J'ai essayé d'utiliser l' map config pour obtenir autour de la nécessité de modifier le jQuery source, mais ne parvenais pas à le faire fonctionner.

mise à jour 2: Une récente question sur le requireJS groupe google m'a fait réaliser que mon explication peut être un peu trompeur, donc je tiens à le préciser. RequireJS ne ré-utiliser un calée dépendance si il a été chargé par requireJS au moins une fois. C'est-à-dire, si vous avez simplement un <script> tag sur la page d'hébergement (disons, 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 ceci dans votre requireJS config:

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

Puis la première fois, vous n' define(['underscore'], function (_) {}); ou var _ = require('underscore');, RequireJS re-charger le trait de soulignement de la bibliothèque plutôt que de ré-utilisation définies précédemment window._, car autant que requireJS sait, vous n'avez jamais chargé trait de soulignement avant. Bien sûr, il peut vérifier pour voir si _ est déjà défini sur la racine de la portée, mais il n'a aucun moyen de vérifier que l' _ qui est déjà là est le même que celui défini dans votre paths config. Par exemple, les deux prototype et jquery astreignent window.$ par défaut, et si requireJS suppose que 'window.$' jQuery est quand il est en fait un prototype, vous allez être dans une mauvaise situation.

Tout cela signifie que si vous mix-and-match de script de chargement des styles comme ça, votre page se retrouver avec 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>

Où le deuxième trait de soulignement instance, qui est chargé par requireJS.

Fondamentalement, une bibliothèque doit être chargé via requireJS pour requireJS avoir connaissance. Cependant, la prochaine fois vous avez besoin de souligner, requireJS va "hey, j'ai déjà chargé, il suffit donc de la main tout ce qui l' exports valeur et ne vous inquiétez pas sur le chargement d'un autre script."

Cela signifie que vous avez deux options réelles. Est ce que je considère être un anti-modèle: il suffit de ne pas utiliser de requireJS pour exprimer les dépendances pour les mondiaux de scripts. C'est, aussi longtemps que la bibliothèque s'attache à un mondial à la racine de contexte, vous serez en mesure d'y accéder, même si cette dépendance n'est pas explicitement requis. Vous pouvez voir pourquoi c'est un anti-pattern - vous avez fondamentalement juste d'éliminer la plupart des avantages de l'utilisation d'un AMD chargeur (explicite dépendance de l'inscription et de la portabilité).

L'autre, la meilleure option est d'utiliser requireJS pour charger tout, au point que la seule balise de script, vous devez créer vous-même est celui qui a initialement charges requireJS. Vous pouvez utiliser des cales, mais 95% du temps, c'est pas vraiment difficile d'ajouter un AMD wrapper pour le script à la place. Il pourrait prendre un peu plus de travail pour convertir tous vos non-AMD bibliothèques AMD compatible, mais une fois que vous avez fait un ou deux, cela devient beaucoup plus facile - je peux prendre un médicament générique jQuery plugin et de le convertir à une AMD module en moins d'une minute. C'est généralement juste une question d'ajouter

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

en haut, et

    return jQuery;
});

au bas. La raison pour laquelle j'ai 'jquery' mappage jQuery plutôt que d' $ , c'est que j'ai remarqué que la plupart des plugins de ces jours sont enveloppés dans une fermeture comme ceci:

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

Et c'est une bonne idée de prêter attention à la portée voulue. Vous pouvez certainement carte 'jquery' à $ directement si, en supposant que le plugin n'est pas en attendant de trouver jQuery au lieu de $. C'est juste la base d'AMD wrapper - plus complexes généralement essayer de détecter ce type de chargeur est utilisé (commonJS vs AMD vs régulière ol' globals) et utiliser une autre méthode de chargement en fonction du résultat. Vous pouvez trouver des exemples de ce assez facilement avec quelques secondes sur google.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X