#javascript #typescript
#javascript #typescript
Вопрос:
Мне нужно динамически загружать x скриптов (скажем, 6 для этого примера) (либо в TypeScript, либо в JavaScript).
Первые 2 скрипта используются последними 4. Поэтому я должен убедиться, что скрипты 1 и 2 загружены до загрузки последних 4.
var scripts = [
{ "path": "f1.js", "isDependency": true },
{ "path": "f2.js", "isDependency": true },
{ "path": "f3.js", "isDependency": false },
{ "path": "f4.js", "isDependency": false },
{ "path": "f5.js", "isDependency": false },
{ "path": "f6.js", "isDependency": false }
];
И у меня есть асинхронная функция, которая загружает скрипты, но у меня возникают проблемы с настройкой async / await или promises, которые ожидают загрузки первых 2 скриптов, а затем позволяют остальным загружаться в любом порядке. порядок.
private async loadJsFileAsync(filePath: string) {
return new Promise((resolve, reject) => {
if (!document.getElementById(filePath)) {
var script = document.createElement('script');
script.onload = function () {
// mark with attribute so we know that it's loaded
script.attributes["data-loaded"] = "true";
resolve();
}
script.onerror = function (err) {
reject(err);
}
script.type = "text/javascript";
script.src = filePath
script.id = filePath;
document.getElementsByTagName('head')[0].appendChild(script);
} else {
// file processed, but check to see it actually loaded
var s1 = <HTMLScriptElement>document.getElementById(filePath);
var loadedAttribute = s1.attributes["data-loaded"] === "true";
if (loadedAttribute) {
resolve();
} else {
// add event listeners (in addition to the existing ones)
s1.addEventListener("load", function () {
resolve();
});
s1.addEventListener("error", function (err) {
reject(err);
});
}
}
});
}
На данный момент я использую асинхронную функцию только с одним требуемым скриптом следующим образом:
this.loadJsFileAsync("f1.js").then(value => {
// load other files
loadJsFile("f3.js");
loadJsFile("f4.js");
loadJsFile("f5.js");
loadJsFile("f6.js");
});
private loadJsFile(filePath: string) {
var script = document.createElement('script');
script.type = "text/javascript";
script.src = filePath this.version();
script.id = filePath;
document.getElementsByTagName('head')[0].appendChild(script);
}
Комментарии:
1. Можете ли вы опубликовать свое использование функции? (Как вы используете await)
2. @mwilson Прямо сейчас я просто использую функцию с одним требуемым скриптом. Опубликовано.
Ответ №1:
Прежде всего, почему бы не использовать requirejs или какой-либо другой загрузчик модулей? Они предоставляют вам возможность объединять ваши скрипты в цепочки, чтобы сначала загружались зависимости. Во-вторых, я думаю, это то, что вы хотите:
Здесь определенно есть место для улучшения и оптимизации, но это должно дать вам результат, который вы ищете.
class Script {
constructor(path, dependencyOrder) {
this.path = path;
this.dependencyOrder = dependencyOrder;
}
}
class ScriptLoader {
constructor() {
this.scripts = [];
}
addScript(script) {
const exists = this.scripts.find( s => s.path.toLowerCase() === script.path.toLowerCase());
if (!exists) {
this.scripts.push(script);
}
}
async loadScripts() {
if (Array.isArray(this.scripts)) {
const orderedScripts = this.orderScriptsByDependency();
let scriptsLoaded = false;
let counter = 0;
while (!scriptsLoaded) {
const _script = orderedScripts[counter]
await this.loadScript(_script, _script.dependencyOrder > -1);
counter = 1;
scriptsLoaded = counter === orderedScripts.length;
}
}
}
async loadScript(script, waitToLoad) {
return new Promise( (resolve, reject) => {
const scriptTag = document.createElement('script');
scriptTag.src = script.path;
if (waitToLoad) {
scriptTag.async = true;
document.body.appendChild(scriptTag);
scriptTag.onload = () => { resolve(); }
} else {
document.body.appendChild(scriptTag);
resolve();
}
} );
}
orderScriptsByDependency() {
if (Array.isArray(this.scripts)) {
return this.scripts.sort( (a, b) => {
return a.dependencyOrder - b.dependencyOrder;
});
}
return null;
}
}
Использование
class IndexView {
constructor() {
this.loader = new ScriptLoader();
this.init();
}
async init() {
this.loader.addScript(new Script('scripts/script0.js', 0));
this.loader.addScript(new Script('scripts/script1.js', 1));
this.loader.addScript(new Script('scripts/script2.js', 2));
await this.loader.loadScripts();
}
}
Комментарии:
1. упс. Забыл вернуть значение в моей функции сортировки. Обновление сейчас
Ответ №2:
Если вы хотите сохранить порядок, вы можете использовать onload
of HtmlScriptElement
подобным образом.
const loadScripts = (urls: string[]): void => {
urls.forEach((url, index) => {
const js = loadScript(url);
if (index < urls.length) {
js.onload = () => {
loadScript(urls[index 1]);
};
}
});
};
const loadScript = (url: string): HTMLScriptElement => {
const js = document.createElement("script");
js.setAttribute("src", url);
document.body.appendChild(js);
return js;
};
И назовите это так:
loadScripts(["script1.js", "script2.js", "script3.js"]);
Комментарии:
1. Это позволит загружать каждый отдельный скрипт по одному. Это долгое время загрузки, если у вас много скриптов…
2. Да — ваше решение лучше с асинхронной загрузкой.
3. @mwilson Я добавил ответ с помощью функции jQuery, которая загружает скрипты, не связывая их.