javascript - requirejs - 多次調用 require 的性能
我想知道在一個有多個模組的專案中使用 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 的效能損耗是多少,以及最佳做法是什麼。
答案#1
這裡對這個問題的答案是「視情況而定」。我以一個在大型應用程式中使用過 RequireJS,但並未徹底閱讀 RequireJS 代碼的人身份來說。(只是指出,完全了解 RequireJS 內部運作的人可能會有不同的解釋。)require 的成本可以分為三種成本情境:
如果模組從未被加載過,
require會從伺服器加載文件、執行該文件、執行模組的工廠函數,並返回模組的參考。(從網絡加載文件的成本通常遠超其他成本。)如果模組已經被加載但從未被 require,
require會執行模組的工廠函數並返回模組的參考。(這通常會發生在優化的應用程式中。)如果模組已經被加載並需要,
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)。最終,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();
[...]
});
[...]
});
答案#2
這取決於你想要的載入行為。
當你需要一個尚未被請求的模組時,將會發出 HTTP 請求來載入該模組。如果該模組至少已被載入一次,它將被存儲在 require 的快取中,因此當你第二次請求該模組時,將使用快取的值。
所以,這是一個在懶加載和急切加載之間的選擇。
需要解決的問題是,每個模組都可以定義它的依賴關係——所以你的第一種方法可能就是「最佳實踐」。最終,你會想使用 r.js 將你的應用程式構建成單一的 JS 檔案。
