论动态添加的script标签如何顺序执行,每天一个小妙招
近来,在折腾跨框架技术栈的国际化通用解决方案,一直这么个问题困扰。
首先,在通用平台页面上配置key、value,后端生成多种语言的js文件,文件中封装好t函数挂在window对象上,这样就不管什么框架都可以直接调用t函数了。但是,这同时也就出现了一个问题,选择的语言不同,src的url也就不同,script标签只能动态生成,但是又要保证这个动态添加的script标签在chunk.js、app.js之前执行。
核心问题
- script标签的src属性需要根据语言不同改变
- 生成的script标签需要在chunk.js、app.js之前执行
实验代码
index.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文件
// 1.js
console.log(1)
// 2.js
console.log(2)
// 3.js
console.log(3)
运行结果
动态标签
src属性无法使用变量,我们能想到的最简单的方法是就是createElement,但是结果总是不如人所愿,修改index.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>
结果
2.js确实被插入到了指定的位置,但是却在最后才执行
Tips: 这时候肯定有同学要说,这是因为动态添加的script标签具有async属性,所以才会异步执行,其实不然,即使增加script.async = false 和 script.defer = false ,2.js也会到最后才执行。因为,任何通过dom api添加的script标签必然会在最后执行。
解决方式一(同步的AJAX请求,eval执行,不推荐)
既然通过dom操作生成script会异步执行,那用同步的AJAX请求不就完了,确实如此,我们实验一下
<!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>
结果
没毛病,确实按顺序执行了,但是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文件的内容
我们来实验一番
<!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>
结果
script标签确实插入到了指定位置,并且脚本按照顺序执行!