【问题标题】:How to achieve lazy loading with RequireJS?RequireJS如何实现延迟加载?
【发布时间】:2012-06-06 08:53:51
【问题描述】:

我们正在使用 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 减少到基本上是一个模块化框架)。不过,我很好奇您是否可以“按需”加载视图和模板等内容。

here 解释了“简化的 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 中是否有黑魔法可以在没有实际触发 doStuff 路由的情况下查找对 require() 的调用?
  • 这是理论上正确的“按需”延迟加载 RequireJS 模块和资源的方法吗?
  • 如果您使用这种表示法,r.js 优化器是否仍能像宣传的那样工作?

【问题讨论】:

    标签: javascript backbone.js requirejs js-amd


    【解决方案1】:

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

    当您使用“糖”语法it uses Function.prototype.toString and a regex 提取对require 的引用,然后在运行函数之前将它们列为依赖项。基本上它变成了以 deps 数组作为第一个参数的正常定义样式。

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

    这是理论上正确的“按需”延迟加载 RequireJS 模块和资源的方法吗?

    如您所见,使用 Sugar 语法将不允许条件加载。我能想到的唯一方法是使用 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 优化项目,那么仅使用糖语法不会产生巨大的开销。

    【讨论】:

      【解决方案2】:

      您可能还想查看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集成。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多