Как добиться ленивой загрузки с помощью RequireJS?

Мы создаем нетривиальное веб-приложение, используя Backbone, RequireJS и Handlebars, и мне просто любопытно. На данный момент каждая из наших моделей выглядит примерно так:

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

где вещь / a, вещь / b имеют свои собственные зависимости, например, от шаблонов Handlebars и т. д. Теперь происходит то, что в моем main.js загружаются и инициализируются все маршрутизаторы «верхнего уровня»; каждый маршрутизатор верхнего уровня имеет набор зависимостей (модели, представления и т. д.), каждая из которых имеет свои собственные зависимости (шаблоны, помощники, утилиты и т. д.). По сути, большая древовидная структура.

Проблема в этом случае заключается в том, что все это дерево разрешается и загружается при загрузке страницы. Я не возражаю против этого, так как в конечном итоге мы пропустим его через оптимизатор и в итоге получим один большой файл (уменьшив RequireJS до структуры модульности). Однако мне любопытно, можете ли вы загружать такие вещи, как представления и шаблоны, «по запросу».

здесь объясняется «упрощенная упаковка CommonJS», поэтому я попробовал это:

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

Однако, глядя на инспектор сети Chrome, кажется, что RequireJS - каким-то образом, даже без запуска маршрута, который запускает обработчик doStuff, по-прежнему загружает зависимость myView. Вопросов:

  • На самом ли деле это возможно? Есть ли в RequireJS черная магия, которая ищет вызовы require() без фактического запуска маршрута doStuff?
  • Является ли это теоретически правильным способом «ленивой загрузки по требованию» модулей и ресурсов RequireJS?
  • Оптимизатор r.js по-прежнему работает так, как рекламируется, если вы используете эту нотацию?

person cthulhu    schedule 06.06.2012    source источник


Ответы (2)


Возможно ли это на самом деле? Есть ли в RequireJS черная магия, которая ищет вызовы require () без фактического запуска маршрута doStuff?

Когда вы используете "сахарный" синтаксис , используется regexFunction.prototype.toString и / a>, чтобы извлечь ваши ссылки на require, а затем перечислить их как зависимости перед запуском функции. По сути, это становится обычным стилем определения с массивом deps в качестве первого аргумента.

Из-за этого ему все равно, где находятся ваши вызовы require и почему условные операторы игнорируются (это также объясняет, почему эти вызовы require должны использовать строковый литерал, а не переменную).

Является ли это теоретически правильным способом «ленивой загрузки по запросу» модулей и ресурсов RequireJS?

Использование синтаксиса сахара не допускает условной загрузки, как вы видели. Единственный способ, который я могу придумать, - это использовать вызов require с массивом deps и обратным вызовом:

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

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

        });
    }

    return {};
});

Единственным недостатком является еще одна вложенная функция, но если вам нужна производительность, это правильный путь.

Будет ли оптимизатор r.js по-прежнему работать так, как было заявлено, если вы используете эту нотацию?

Если вы используете синтаксис «сахар», то да, оптимизатор будет работать нормально. Пример:

modules / test.js

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

    return {
        bla: true
    }
});

После компиляции r.js это выглядит так:

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

    return {
        bla: true
    }
});

В заключение вы можете загружать материал условно, но, как вы упомянули, если вы намерены оптимизировать проект с помощью r.js, тогда просто использование синтаксиса сахара не вызывает огромных накладных расходов.

person Simon Smith    schedule 06.06.2012

Вы также можете попробовать require-lazy.

Он имеет компонент времени выполнения и компонент времени сборки. Компонент времени выполнения позволяет вам лениво потребовать модуль как (обратите внимание на плагин lazy!):

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

В предыдущем контексте mymodule - это обещание, реальный модуль будет загружен с get() и будет доступен в обратном вызове then():

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

Require-lazy интегрируется с r.js для автоматического создания «пакетов» файлов Javascript. Он также выполняет автоматическую очистку кеша для пакетов. Есть несколько примеров, чтобы понять. Также есть Grunt и Интеграция с Bower.

person Nikos Paraskevopoulos    schedule 14.03.2014