javascript – 加载动态添加的脚本标记的顺序

我有一个打字稿应用程序,动态添加指向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文件中静态定义脚本标记时的排序方式)?

附:我想避免使用onload回调来解决这个问题,因为我发现我的应用程序性能下降了.我的第二个脚本文件非常大,我假设这导致了降级.

解决方法:

有一些方法可以克服这一要求.现在我可以建议如下:

>使用库注入依赖项(AMD或CommonJS模块)
>以编程方式创建脚本标记并将属性设置为async = false.
>以编程方式创建脚本标记并设置回调onload,以便在脚本异步加载时做出反应.默认情况下设置属性async = true. (见下面的例子)
>如果您不喜欢以前的选项并且允许修改要注入的脚本,则在脚本末尾添加一行,其中包含跟踪加载的脚本的对象/数组.
>作为最后一个资源,您可以将脚本作为文本获取,使用所需顺序的脚本构建一个字符串(将每个文本脚本包装在一个IIFE内),最后通过可怕且危险的eval()执行文本脚本.
>最糟糕的选择是使用setInterval来检查脚本是否已执行.我添加了最后一个选项只是因为我看到了一些使用这种糟糕技术的解决方案.

如果你在一个大项目中工作,我会推荐第一个选项.但是如果你有一个小项目,你可以选择第三个选项.为什么?

让我们考虑在第二个选项中,通过设置
属性async = false将导致浏览器阻止呈现,直到脚本执行完毕(不良做法).

我想推荐一个关于脚本加载器的阅读:Deep dive into the murky waters of script loading,半小时值得花钱!

我写了一个用于管理脚本注入的小模块的示例,这是背后的基本思想:

let _scripts = [
  'path/to/script1.js',
  'path/to/script2.js',
  'path/to/script3.js'
];

function createScriptTag() {
  // gets the first script in the list
  let script = _scripts.shift();
  // all scripts were loaded
  if (!script) return;
  let js = document.createElement('script');
  js.type = 'text/javascript';
  js.src = script;
  js.onload = (event) => {
    // loads the next script
    createScriptTag();
  };
  let s = document.getElementsByTagName('script')[0];
  s.parentNode.insertBefore(js, s);
}

在此plunker中,您可以按特定顺序异步测试脚本注入:

> https://plnkr.co/edit/b9O19f

主要思想是创建一个API,通过公开以下方法,允许您与脚本进行交互以进行注入:

> addScript:接收url或脚本的url数组.
> load:按特定顺序运行加载脚本的任务.
> reset:清除脚本数组,或取消脚本加载.
> afterLoad:加载每个脚本后执行回调.
> onComplete:在加载所有脚本后执行回调.

我喜欢Fluent Interface或方法链接方式,然后我以这种方式构建模块:

  jsuLoader
    .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”执行相同的操作,并在加载所有脚本之后执行onComplete ()回调被执行.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐