Skip to content
On this page

论动态添加的script标签如何顺序执行,每天一个小妙招

近来,在折腾跨框架技术栈的国际化通用解决方案,一直这么个问题困扰。
首先,在通用平台页面上配置key、value,后端生成多种语言的js文件,文件中封装好t函数挂在window对象上,这样就不管什么框架都可以直接调用t函数了。但是,这同时也就出现了一个问题,选择的语言不同,src的url也就不同,script标签只能动态生成,但是又要保证这个动态添加的script标签在chunk.js、app.js之前执行。

核心问题

  • script标签的src属性需要根据语言不同改变
  • 生成的script标签需要在chunk.js、app.js之前执行

实验代码

index.html

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="./1.js"></script>
    <script src="./2.js"></script>
    <title>Test</title>
  </head>
  <body>
    <div id="app">APP</div>
    <script src="./3.js"></script>
  </body>
</html>

js文件

js
// 1.js
console.log(1)

// 2.js
console.log(2)

// 3.js
console.log(3)

运行结果

Snipaste_2022-09-07_16-20-11.jpg

动态标签

src属性无法使用变量,我们能想到的最简单的方法是就是createElement,但是结果总是不如人所愿,修改index.html文件如下

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="./1.js"></script>
    <!-- 修改 start -->
    <script>
      const script = document.createElement('script')
      script.src = './2.js'
      document.head.appendChild(script)
    </script>
    <!-- 修改 end -->
    <title>Test2</title>
  </head>
  <body>
    <div id="app">APP</div>
    <script src="./3.js"></script>
  </body>
</html>

结果

image.png

image.png

2.js确实被插入到了指定的位置,但是却在最后才执行

Tips: 这时候肯定有同学要说,这是因为动态添加的script标签具有async属性,所以才会异步执行,其实不然,即使增加script.async = false 和 script.defer = false ,2.js也会到最后才执行。因为,任何通过dom api添加的script标签必然会在最后执行。

解决方式一(同步的AJAX请求,eval执行,不推荐)

既然通过dom操作生成script会异步执行,那用同步的AJAX请求不就完了,确实如此,我们实验一下

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="./1.js"></script>
    <!-- 修改 start -->
    <script>
      var xhr = new XMLHttpRequest()
      xhr.open('get', './2.js', false)
      xhr.onreadystatechange = function () {
        if (this.readyState == 4 && this.status === 200) {
          window.eval(this.responseText)
        }
      }
      xhr.send()
    </script>
    <!-- 修改 end -->
    <title>Test2</title>
  </head>
  <body>
    <div id="app">APP</div>
    <script src="./3.js"></script>
  </body>
</html>

结果

image.png

没毛病,确实按顺序执行了,但是AJAX请求代码不够简单,且不支持跨域,如果我的通用平台不是同域部署,那这个方式也没法用,万恶的eval也会导致代码安全检查不通过,那有什么更优的解决方案么?

解决方案二(推荐)

dom操作会导致script最后才执行,那有什么方法可以不操作dom又可以添加一个script标签么,纵观js api还真有这么个神奇的方法,并且这个方法大家初学前端的时候就必然学过,想想最开始我们如何在页面上显示hello world? 没错就是document.write!

document.write可以向打开的文档写入文本,并且文本会被作为HTML解析,如果插入普通dom元素,在head中使用则会追加到body顶部,在body中使用,则会追加到当前位置下方。而如果是script标签,不管是在哪里使用都会追加到当前位置下方。如果在onload之后才使用,则会清除body中的内容进行重写。

浏览器解析html文件时,当前打开的文档就是这个html,此时使用document.write会直接修改html文件的内容

我们来实验一番

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="./1.js"></script>
    <!-- 修改 start -->
    <script>
      document.write('<script src="./2.js"><\/script>')
    </script>
    <!-- 修改 end -->
    <title>Test2</title>
  </head>
  <body>
    <div id="app">APP</div>
    <script src="./3.js"></script>
  </body>
</html>

结果

image.png

image.png

script标签确实插入到了指定位置,并且脚本按照顺序执行

上次更新于: