如何以特定顺序动态加载多个脚本?

时间:2019-04-09 22:59:44

标签: javascript typescript

我需要动态地(在TypeScript和JavaScript中)加载x个脚本(本示例中为6个)。

后2个脚本使用前2个脚本,因此我必须确保在加载后4个脚本之前先加载脚本1和2。

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 }
];

我有一个异步函数来加载脚本,但是我在设置异步/等待或承诺先等待前两个脚本加载然后让其他脚本以任何顺序加载时遇到麻烦。

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);
}   

2 个答案:

答案 0 :(得分: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();
    }
}

Requirejs

答案 1 :(得分:1)

如果要保留订单,可以像这样利用onload中的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"]);