web-dev-qa-db-fra.com

Comment réaliser un chargement paresseux avec RequireJS?

Nous construisons une application Web non-trival utilisant Backbone, RequireJS et Handlebars, et bien, je suis juste curieux. Pour le moment, chacun de nos modèles sorta ressemble à ceci:

define(['Backbone', 'js/thing/a', 'js/thing/b', 'js/lib/bob'], function(a, b, bob) {
  return Backbone.Router.extend({
    // stuff here
  });
});

où chose/a, chose/b ont leurs propres dépendances, par exemple sur les modèles de guidons, etc. Ce qui se passe maintenant, c'est que dans mon main.js, tous les routeurs de "haut niveau" sont chargés et initialisés; chaque routeur de niveau supérieur possède un ensemble de dépendances (modèles, vues, etc.) qui ont chacune leurs propres dépendances (modèles, assistants, utilitaires, etc.). Fondamentalement, une grande structure arborescente.

Le problème dans ce cas est que cette arborescence entière est résolue et chargée au chargement de la page. Cela ne me dérange pas, car nous l'exécuterons finalement dans l'optimiseur et nous retrouverons avec un gros fichier unique (réduisant RequireJS à un cadre de modularisation). Cependant, je suis curieux de savoir si vous pouvez charger des éléments tels que des vues et des modèles "à la demande".

Il y a le "wrapper CommonJS simplifié" expliqué ici , j'ai donc essayé cela:

define(function(require) {
  Backbone = require('Backbone');
  return Backbone.Router.extend({
    doStuff: function() {
      var MyView = require('js/myView');
      new MyView().render();
    }
  });
});

Cependant, en regardant l'inspecteur de réseau de Chrome, il semble que RequireJS - en quelque sorte, même sans déclencher l'itinéraire qui déclenche le gestionnaire doStuff - charge toujours la dépendance myView. Des questions:

  • Est-ce réellement possible? Existe-t-il des magicks noirs dans RequireJS qui recherchent les appels à require() sans déclencher réellement la route doStuff?
  • Est-ce la façon théoriquement correcte de procéder au chargement "à la demande" et paresseux des modules et des ressources RequireJS?
  • L'optimiseur r.js fonctionne-t-il toujours comme annoncé si vous utilisez cette notation?
60
fwielstra

Est-ce réellement possible? Existe-t-il des magicks noirs dans RequireJS qui recherchent les appels à require () sans déclencher réellement la route doStuff?

Lorsque vous utilisez la syntaxe "sucre" elle utilise Function.prototype.toString et une expression régulière pour extraire vos références à require, puis les répertorie en tant que dépendances avant d'exécuter la fonction. Fondamentalement, il devient le style normal de définir avec un tableau de dep comme premier argument.

Pour cette raison, peu importe où se trouvent vos appels requis et c'est pourquoi les instructions conditionnelles sont ignorées (cela explique également pourquoi ces appels require doivent utiliser un littéral de chaîne et non une variable).

Est-ce la façon théoriquement correcte de procéder "à la demande", le chargement paresseux des modules et des ressources RequireJS?

L'utilisation de la syntaxe Sugar ne permettra pas le chargement conditionnel comme vous l'avez vu. La seule façon dont je peux penser du haut de ma tête est d'utiliser un appel require avec un tableau de deps et un rappel:

define(function(require) {
    var module1 = require('module1');

    // This will only load if the condition is true
    if (true) {
        require(['module2'], function(module2) {

        });
    }

    return {};
});

Seul inconvénient est une autre fonction imbriquée, mais si vous recherchez des performances, il s'agit d'un itinéraire valide.

L'optimiseur r.js fonctionne-t-il toujours comme annoncé si vous utilisez cette notation?

Si vous utilisez la syntaxe "sucre", alors oui, l'optimiseur fonctionnera bien. Un exemple:

modules/test.js

define(function(require) {
    var $ = require('jquery');
    var _ = require('underscore');

    return {
        bla: true
    }
});

Une fois compilé par r.js, cela ressemble à ceci:

define('modules/test', ['require', 'jquery', 'underscore'], function(require) {
    var $ = require('jquery');
    var _ = require('underscore');

    return {
        bla: true
    }
});

En conclusion, vous pouvez charger des choses de manière conditionnelle, mais comme vous l'avez mentionné, si vous avez l'intention d'optimiser le projet avec r.js, il n'y a pas d'énormes frais généraux en utilisant simplement la syntaxe de sucre.

50
Simon Smith

Vous pouvez également consulter require-lazy .

Il a un composant d'exécution et un composant de construction. Le composant d'exécution vous permet de demander paresseusement un module comme (notez le plugin lazy!):

define(["lazy!mymodule"], function(mymodule) {
    ...
});

Dans le contexte précédent, mymodule est un promesse , le module réel sera chargé avec get() et sera rendu disponible dans la then() rappeler:

mymodule.get().then(function(m) {
    // here m is the real mymodule
});

Require-lazy s'intègre à r.js pour créer automatiquement des "bundles" de fichiers Javascript. Il gère également automatiquement le contournement du cache pour les bundles. Il existe plusieurs exemples pour se faire une idée. Il y a aussi l'intégration Grunt et Bower .

3