在《浏览器环境下JavaScript脚本加载与执行探析之defer与async特性》中,我们研究了延迟脚本(defer)和异步脚本(async)的执行时机、浏览器支持情况、浏览器bug以及其他的细节问题。而除了defer和async特性,动态脚本和Ajax脚本注入也是两种常用的创建无阻塞脚本的方法。总的来看,这两种方法都能达到脚本加载不影响页面解析和渲染的作用,但是在不同的浏览器中,这两种技术所创建的脚本的执行时机还是有一定差异,今天我们再来探讨一下通过动态脚本技术和Ajax注入的脚本在这些方面的特性。
代码准备:
我们使用《浏览器环境下JavaScript脚本加载与执行探析之代码执行顺序》2.3节中的loadScript函数来添加动态脚本,同时使用这篇文章2.4节中的loadXhrScript函数来实现Ajax脚本注入。我们把这两个函数都放在util.js中。
另外,本文使用的CHROME的版本为47.0.2526.80,firefox的版本为43.0.4,opera版本为30.0.1835.125。
1 动态脚本
1.1动态脚本的执行时机问题
我们在《浏览器环境下JavaScript脚本加载与执行探析之defer与async特性》中2.3节DEMO的基础上,增加三个外部js文件:
dynamic1.js
test += "我是head外部动态脚本\n";
dynamic2.js
test += "我是body外部动态脚本\n";
dynamic3.js
test += "我是底部外部动态脚本\n";
1.1.1 DEMO1:动态脚本的执行时机初探
HTML的代码为:
<!DOCTYPE html> <html> <head> <meta charset="UTF-"/> <title>Dynamic Script Test</title> <script src="https://lib.sinaapp.com/js/jquery/../jquery-...min.js"></script> <script src="https://www.jb51.net/util.js"></script> <script type="text/javascript">var test = "";</script> <script> loadScript("dynamic.js"); </script> <script> test += "我是head内部脚本\n"; </script> <script src="https://www.jb51.net/article/.js" type="text/javascript"></script> </head> <body> <button>点击一下</button> <script> loadScript("dynamic.js"); </script> <script src="https://www.jb51.net/article/.js" type="text/javascript"></script> </body> <script> loadScript("dynamic.js"); </script> <script src="https://www.jb51.net/article/.js" type="text/javascript"></script> <script> $(function(){ test += "我是DOMContentLoaded里面的脚本\n"; }) window.onload = function(){ test += "我是window.onload里面的脚本\n"; var button = document.getElementById("test"); button.onclick = function(){ console.log(test); } } </script>
在代码中,我们先后将3个动态脚本文件加入到HTML的<head>标签中,同时通过与正常外部脚本和内部脚本的执行来进行比较,我们看一下不同浏览器中的执行结果:
注:firefox和opera中执行结果可能会变化
从上面的例子中,我们可以看出,在不同浏览器中,动态脚本的执行时机差异还是比较大的,但以下两点是可以明确的:
[1]动态脚本确实可以起到不阻塞后续脚本的作用,即延迟作用,但是这个延迟作用却不一定能够持续到所有的正常脚本都执行完毕之后,也无法保证能够延迟到DOMContentLoaded之后
[2]动态脚本执行的先后顺序是无法保证的,这一点在https://www.jb51.net/article/77920.htm这篇文章以及后续的几篇文章中进行了详细的解释
从这个DEMO中还可以看出,动态脚本的执行时机具有较大的不确定性,虽然在DEMO1中,动态脚本都在DOMContentLoaded事件之后执行,但却也并不意味着动态脚本就不会阻塞DOMContentLoaded,我们通过DEOM2来看一下:
1.1.2 DEMO2:动态脚本对DOMContentLoaded的阻塞
我们把DEMO1中的第27行内码修改为:
<script src="/delayfile.php?url=http://localhost/js/load/3.js&delay=5" type="text/javascript"></script>
我们利用《浏览器环境下JavaScript脚本加载与执行探析之代码执行顺序》中的delayfile.php,将3.js的返回延迟5秒钟,我们知道,如果是defer延迟脚本,无论正常外部脚本延迟了多长时间,defer脚本还是会在正常外部脚本之后执行的,但是动态脚本却不是这样了,我们看一下修改后的代码在浏览器中的执行顺序:
注:firefox和opera中执行结果可能会变化