如何使用RequireJS实现懒加载?

61
我们正在使用Backbone、RequireJS和Handlebars构建一个不简单的Web应用程序,目前,我们的每个模型看起来都像这样:
define(['Backbone', 'js/thing/a', 'js/thing/b', 'js/lib/bob'], function(a, b, bob) {
  return Backbone.Router.extend({
    // stuff here
  });
});

thing/a和thing/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优化器是否仍按宣传的那样工作?
2个回答

52

这真的可能吗?在RequireJS中是否有黑魔法可以查找对require()的调用而不实际触发doStuff路由?

当您使用“sugar”语法它使用Function.prototype.toString 和一个正则表达式来提取对 require 的引用,然后将它们列为依赖项,然后运行函数。基本上,它变成了定义的常规样式,其中数组deps作为第一个参数。

因此,它不关心您的require调用位于何处,这就是为什么条件语句被忽略的原因(这也解释了为什么这些 require 调用必须使用字符串文字,而不是变量)。

这是进行按需惰性加载RequireJS模块和资源的理论正确方式吗?

使用sugar语法无法实现条件加载,正如你所见。我能想到的唯一方法是使用一个包含依赖项和回调函数的require调用的数组。
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优化器是否仍然按照广告所述工作?

如果使用“sugar”语法,则优化器将正常工作。例如:

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优化项目,那么仅使用糖语法并不会带来太大的开销。


4

您可能也想查看require-lazy

它有一个运行时组件和一个构建时组件。运行时组件允许您使用(请注意lazy!插件)作为延迟加载模块:

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

在先前的上下文中,mymodule 是一个Promise,真正的模块将会通过get()被加载,并在then()回调中被使用。
mymodule.get().then(function(m) {
    // here m is the real mymodule
});

Require-lazy与r.js集成,可自动创建JavaScript文件的“捆绑包”。它还处理捆绑包的自动缓存清除。有几个示例可供参考。还有GruntBower的集成。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接