[英]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.