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 优化器是否仍然像宣传的那样工作?
4

2 回答 2

52

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

当您使用它使用的“糖”语法Function.prototype.toString和正则表达式来提取您的引用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 优化器是否仍然像宣传的那样工作?

如果您使用的是 'sugar' 语法,那么是的,优化器可以正常工作。一个例子:

模块/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 优化项目,那么仅使用糖语法就不会产生巨大的开销。

于 2012-06-06T12:34:30.857 回答
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集成。

于 2014-03-14T09:36:37.397 回答