6

我想知道在具有多个模块的项目中使用 RequireJS 的正确方法是什么,关于具有较少依赖项的多个 require 调用与具有所有依赖项的单个 require 的性能。

让我们看一下,对于一个应用程序,我需要加载一些模块:gmaps, jquery, module1, module2, module3. 一些模块的使用是完全独立的。因此,问题是推荐以下哪种替代方案(假设此代码是加载到页面中的主模块):

require(['gmaps'], function(gmaps){
   gmaps.use();
});

require(['jquery','module1'], function(jquery, module1){
   module1.use();
});

require(['jquery','module2'], function(jquery, module2){
   module2.use();
});

require(['jquery','module3'], function(jquery, module3){
   module3.use();
});

对比

require(['jquery','module1','module1','module1','gmaps'], function(jquery, module1,module2,module3,gmaps){
   module1.use();
   module2.use();
   module3.use();
   gmaps.use();
});

换句话说,什么是性能损失,什么是require最佳实践。

4

3 回答 3

6

这里问题的答案是“视情况而定”。我说的是在大型应用程序中使用过 RequireJS 但没有彻底阅读过 RequireJS 代码的人。(只是指出,从里到外了解 RequireJS 内幕的人可能会做出与我不同的解释。)成本require可以分解为 3 种成本场景:

  1. 如果模块从未被加载,require则从服务器加载文件,执行文件,执行模块的工厂函数并返回对模块的引用。(从网络加载文件的成本通常使其他成本相形见绌。)

  2. 如果模块已经加载但从不需要,require则执行模块的工厂函数并返回对该模块的引用。(这通常发生在优化的应用程序中。)

  3. 如果模块已经被加载并且需要,require则返回对该模块的引用。

成本方案 1 > 成本方案 2 > 成本方案 3。

首先,让我们考虑每个文件有一个模块的情况。应用程序未优化。我有一个名为的模块module1,在蓝月亮中需要一次。它在主应用程序中的使用可以这样建模:

define(["module1", <bunch of other required modules>],
    function (module1, <bunch of other modules variables>) {

    [...]

    if (rare_condition_happening_once_in_a_blue_moon)
        module1.use();

    [...]
});

在这里,即使我不使用模块,我也总是为成本场景 1 付出代价。最好这样做:

define([<bunch of required modules>],
    function (<bunch of module variables>) {

    [...]

    if (rare_condition_happening_once_in_a_blue_moon)
        require(["module1"], function (module1) {
            module1.use();
        });

    [...]
});

这样一来,我就可以为加载模块付出一次代价。

现在,如果我需要module重复使用怎么办?这可以建模为:

define(["module1", <bunch of other required modules>],
    function (module1, <bunch of other modules variables>) {

    [...]

    for(iterate a gazillion times)
        module1.use();

    [...]
});

在这种情况下,成本方案 1 支付一次,仅此而已。如果我require这样使用:

define([<bunch of required modules>],
    function (<bunch of module variables>) {

    [...]

    for(iterate a gazillion times)
        require(["module1"], function (module1) {
            module1.use();
        });

    [...]
});

我支付了 1 号成本方案一次3次(无数次 - 1)成本方案号 3。归根结底,是否module1应将其包含在呼叫的要求中define或单独的require呼叫中取决于具体情况你的申请。

如果应用程序已通过使用r.js或自制优化进行了优化,则分析会发生变化。如果应用程序经过优化,所有模块都在一个文件中,则每次在上述情况下支付成本方案 1 时,您将改为支付成本方案 2。

为了完整起见,我将补充一点,如果您不提前知道您可能要加载的模块,则使用require是必要的。

define([<bunch of other required modules>],
    function (<bunch of other modules variables>) {

    [...]

    require(<requirements unknown ahead of time>, function(m1, m2, ...) {
        m1.foo();
        m2.foo();
        [...]
    });

    [...]
});
于 2013-11-10T20:43:37.780 回答
1

第二种方法在我看来违背了require js的主要目的,即不同程序逻辑的分离。如果您将代码放在一个“模块”中,为什么还要使用 requirejs?.就性能而言,重要的是定义您对性能的定义。如果是您的代码执行速度,那么应该没有明显的差异。加载所有必需的模块后,代码将立即执行。另一方面,如果您所说的性能是指:当代码执行开始时 - 那么显然在第二种方法中,您将因对所需代码所在的文件的额外请求而产生额外的开销。

编辑:

在仔细研究了这个问题之后,我的答案的第一部分并不适合它。该代码实际上是在引用外部模块,因此第二个选项并不违背 requirejs 库的目的,并且实际上是更好的方法,只要组合其依赖项的模块不尝试做太多事情。

于 2013-11-10T17:35:32.417 回答
1

这取决于您想要的加载行为。

当您需要尚未需要的模块时,将发出 HTTP 请求以加载所需的模块。如果需要的模块至少被加载过一次,它会存储在 require 的缓存中,所以当你第二次需要模块时,它将使用缓存的值。

因此,它可以在延迟加载或急切加载之间进行选择。

require 试图解决的问题是每个模块都可以定义它的依赖关系 - 所以你的第一种方法可能是“最佳实践”。最后,您希望使用 r.js 将您的应用程序构建到单个 JS 文件中。

于 2013-11-10T17:21:55.233 回答