[英]Load ordering of dynamically added script tags
我有一個打字稿應用程序,可以動態添加指向 JS 文件的腳本標簽。 由於某些限制,我無法在 html 文件中靜態定義這些腳本標簽,因此我通過 typescript 動態添加它們,如下所示:
for (let src of jsFiles) {
let sTag = document.createElement('script');
sTag.type = 'text/javascript';
sTag.src = src;
sTag.defer = true;
document.body.appendChild(script);
}
現在,我注意到,當我動態添加腳本標簽時,它們似乎並不能保證它們的加載順序。 不幸的是, jsFiles
數組具有相互依賴的腳本。 因此,數組中的第二個腳本只能在第一個腳本完全加載后加載。 第二個腳本引用了在第一個腳本中定義的函數。 有沒有一種方法可以在動態添加腳本時指定腳本的排序和執行順序(類似於在 html 文件中靜態定義腳本標簽時的排序方式)?
PS 我想避免使用 onload 回調來解決這個問題,因為我注意到我的應用程序的性能下降。 我的第二個腳本文件非常大,我假設這導致了降級。
我可以提到一些替代方案來克服該要求:
import
/ export
,或 CommonJS: require()
。script
標記並設置回調onload
以便在異步加載腳本時做出反應。 屬性async = true
是默認設置的。object
或array
,用於跟蹤已加載的腳本。XMLHttpRequest
)獲取,然后按照所需的順序使用腳本構建一個string
,最后通過eval()
執行文本腳本setInterval
來檢查腳本是否已經執行。我建議選擇第一個選項。 但出於學術目的,我將說明第二種選擇:
以編程方式創建
script
標記並設置回調onload
以便在異步加載腳本時做出反應。
我想推薦一本關於腳本加載器的讀物: 深入了解腳本加載的陰暗面,值得花半個小時!
下面的例子是一個管理腳本注入的小模塊,這是它背后的基本思想:
let _scriptsToLoad = [
'path/to/script1.js',
'path/to/script2.js',
'path/to/script3.js'
];
function createScriptElement() {
// gets the first script in the list
let script = _scriptsToLoad.shift();
// all scripts were loaded
if (!script) return;
let js = document.createElement('script');
js.type = 'text/javascript';
js.src = script;
js.onload = onScriptLoaded;
let s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(js, s);
}
function onScriptLoaded(event) {
// loads the next script
createScriptElement();
};
在此plunker 中,您可以按特定順序異步測試腳本注入:
主要思想是創建一個 API,允許您通過公開以下方法與要注入的腳本進行交互:
addScript
:接收要加載的每個腳本的 URL 或 URL 列表。load
:運行任務以按指定順序加載腳本。reset
:清除腳本數組,或取消腳本加載。afterLoad
:在每個腳本加載后執行的回調。onComplete
:在所有腳本加載后執行的回調。我喜歡Fluent Interface或方法鏈技術,所以我以這種方式構建了模塊:
scriptsLoader
.reset()
.addScript("script1.js")
.addScript(["script2.js", "script3.js"])
.afterLoad((src) => console.warn("> loaded from jsuLoader:", src))
.onComplete(() => console.info("* ALL SCRIPTS LOADED *"))
.load();
在上面的代碼中,我們首先加載"script1.js"
文件,並執行afterLoad()
回調,接下來,對"script2.js"
和"script3.js"
做同樣的"script3.js"
,在所有腳本加載完成后, onComplete()
回調被執行。
我想避免使用 onload 回調來解決這個問題,因為我注意到我的應用程序性能下降。
如果您想要訂購文件......您需要等待每個文件的加載。 沒辦法。
這是我曾經寫過的一個實用函數:
/**
* Utility : Resolves when a script has been loaded
*/
function include(url: string) {
return new Promise<{ script: HTMLScriptElement }>((resolve, reject) => {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = function() {
resolve({ script });
};
document.getElementsByTagName('head')[0].appendChild(script);
});
}
對於使用 jQuery 的任何人,我都改進了 @adeneo 腳本,因此它將按指定順序加載所有腳本。 它不進行鏈式加載,因此速度非常快,但如果您想要更快,請更改 50 毫秒的等待時間。
$.getMultiScripts = function(arr, path) {
function executeInOrder(scr, code, resolve) {
// if its the first script that should be executed
if (scr == arr[0]) {
arr.shift();
eval(code);
resolve();
console.log('executed', scr);
} else {
// waiting
setTimeout(function(){
executeInOrder(scr, code, resolve);
}, 50);
}
}
var _arr = $.map(arr, function(scr) {
return new Promise((resolve) => {
jQuery.ajax({
type: "GET",
url: (path || '') + scr,
dataType: "text",
success: function(code) {
console.log('loaded ', scr);
executeInOrder(scr, code, resolve);
},
cache: true
});
});
});
_arr.push($.Deferred(function( deferred ){
$( deferred.resolve );
}));
return $.when.apply($, _arr);
}
如何使用:
var script_arr = [
'myscript1.js',
'myscript2.js',
'myscript3.js'
];
$.getMultiScripts(script_arr, '/mypath/').done(function() {
// all scripts loaded
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.