requirejs - 要求的多个调用的性能

时间:2013-11-10 17:17:47

标签: javascript performance requirejs amd

我想知道在具有多个模块的项目中使用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();
});

VS

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

换句话说,require的性能损失是什么,这是最好的做法。

3 个答案:

答案 0 :(得分:6)

这里问题的答案是“这取决于”。我说的是在大型应用程序中使用RequireJS但是的人彻底阅读了RequireJS的代码。 (只是指出那些从内到外都知道RequireJS内部的人可能会解释与我不同的方式。)require的成本可以分解为3种成本方案:

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

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

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

  4. 成本方案1&gt; <成本方案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一次 a(数十亿次 - 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();
            [...]
        });
    
        [...]
    });
    

答案 1 :(得分:1)

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

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

所以,它是懒惰或急切加载之间的选择。

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

答案 2 :(得分:1)

在我看来,第二种方法是针对require js的主要目的,即不同程序逻辑的分离。如果将代码放在一个“模块”中,为什么要使用requirejs? 。在性能方面,定义您的性能定义非常重要。如果是代码执行的速度,那么应该没有明显的区别。加载所有必需的模块后,代码将立即执行。另一方面,如果性能的意思是:当代码执行开始时 - 显然在第二种方法中,由于对所需代码所在的文件的额外请求而导致额外的开销。

编辑:

仔细研究这个问题之后,我的答案的第一部分并不合适。代码实际上是引用外部模块,所以第二个选项不是针对requirejs库的目的,只要结合其依赖关系的模块没有尝试做太多,实际上是更好的方法。