Javascript 如何使用 RequireJS 实现延迟加载?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10911044/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 03:32:18  来源:igfitidea点击:

How to achieve lazy loading with RequireJS?

javascriptbackbone.jsrequirejsjs-amd

提问by fwielstra

We're building a non-trival web application using Backbone, RequireJS and Handlebars, and well, I'm just curious. At the moment, each of our models sorta looks like this:

我们正在使用 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
  });
});

where thing/a, thing/b both have their own dependencies, for example on Handlebars templates, etc. What happens now is that in my main.js, all of the 'top-level' routers are loaded and initialized; each top-level router has a set of dependencies (models, views, etc) which each have their own dependencies (templates, helpers, utils, etc). Basically, a big tree structure.

其中 thing/a、thing/b 都有自己的依赖关系,例如对 Handlebars 模板等。现在发生的是,在我的 main.js 中,所有“顶级”路由器都已加载并初始化;每个顶级路由器都有一组依赖项(模型、视图等),每个都有自己的依赖项(模板、帮助程序、实用程序等)。基本上,一个大树结构。

The problem in this case is that this entire tree is resolved and loaded on page load. I don't mind that per sé, as we'll run it through the optimizer eventually and end up with one big single file (reducing RequireJS to basically a modularization framework). However, I am curious whether you can load stuff like views and templates 'on demand'.

在这种情况下的问题是整个树在页面加载时被解析和加载。我不介意这本身,因为我们最终将通过优化器运行它并最终得到一个大的单个文件(将 RequireJS 简化为基本上模块化的框架)。但是,我很好奇您是否可以“按需”加载视图和模板等内容。

There is the "simplified CommonJS wrapping" explained here, so I tried that:

还有就是“简化CommonJS的包裹解释说:”在这里,所以我试过了:

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

However, looking at Chrome's network inspector, it seems that RequireJS - somehow, even without triggering the route that triggers the doStuff handler - still loads the myViewdependency. Questions:

然而,看看 Chrome 的网络检查器,似乎 RequireJS - 不知何故,即使没有触发触发 doStuff 处理程序的路由 - 仍然加载myView依赖项。问题:

  • Is this actually possible? Are there black magicks in RequireJS that looks for calls to require()without actually triggering the doStuffroute?
  • Is this the theoretically correct way of going about 'on-demand', lazy loading of RequireJS modules and resources?
  • Does the r.js optimizer still work as advertised if you use this notation?
  • 这真的可能吗?RequireJS 中是否有黑魔法可以在require()不实际触发doStuff路由的情况下查找调用?
  • 这是进行“按需”延迟加载 RequireJS 模块和资源的理论上正确的方法吗?
  • 如果您使用这种表示法,r.js 优化器是否仍能像宣传的那样工作?

回答by Simon Smith

Is this actually possible? Are there black magicks in RequireJS that looks for calls to require() without actually triggering the doStuff route?

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

When you use the 'sugar' syntax it uses Function.prototype.toStringand a regexto extract your references to requireand then lists them as dependencies before running the function. Basically it becomes the normal style of define with an array of deps as the first argument.

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

Because of this, it doesn't care where your require calls are and that's why conditional statements are ignored (it also explains why those requirecalls have to use a string literal, and not a variable).

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

Is this the theoretically correct way of going about 'on-demand', lazy loading of RequireJS modules and resources?

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

Using the sugar syntax won't allow conditional loading as you've seen. The only way I can think of off the top of my head is to use a requirecall with an array of deps and a callback:

正如您所见,使用糖语法不允许条件加载。我能想到的唯一方法是使用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 {};
});

Only downside is another nested function but if you're after performance then this is a valid route.

唯一的缺点是另一个嵌套函数,但如果您追求性能,那么这是一条有效的路线。

Does the r.js optimizer still work as advertised if you use this notation?

如果您使用这种表示法,r.js 优化器是否仍能像宣传的那样工作?

If you're using the 'sugar' syntax then yes, the optimiser will work fine. An example:

如果您使用的是“糖”语法,那么是的,优化器将正常工作。一个例子:

modules/test.js

模块/test.js

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

    return {
        bla: true
    }
});

Once compiled by r.js this looks like:

一旦被 r.js 编译,这看起来像:

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

    return {
        bla: true
    }
});

In conclusion you can load stuff conditionally, but as you mentioned, if you intend to optimise the project with r.js then there isn't a huge overhead in just using the sugar syntax.

总之,您可以有条件地加载内容,但正如您所提到的,如果您打算使用 r.js 优化项目,那么仅使用糖语法就不会产生巨大的开销。

回答by Nikos Paraskevopoulos

You may also want to check out require-lazy.

您可能还想查看require-lazy

It has a runtime component and a build time component. The runtime component allows you to lazily require a module as (note the lazy!plugin):

它有一个运行时组件和一个构建时组件。运行时组件允许你懒惰地要求一个模块(注意lazy!插件):

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

In the previous context, mymoduleis a promise, the real module will be loaded with get()and will be made available in the then()callback:

在前面的上下文中,mymodule是一个promise,真正的模块将被加载get()并在then()回调中可用:

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

Require-lazy integrates with r.js to automatically create "bundles" of Javascript files. It also handles automatically cache-busting for the bundles. There are several examples to get an idea. There is also Gruntand Bowerintegration.

Require-lazy 与 r.js 集成以自动创建 Javascript 文件的“包”。它还自动处理包的缓存破坏。有几个例子可以得到一个想法。还有GruntBower集成。