- Published on
JS的加载、执行顺序
- Authors
- Name
- Will Liu
- @satlxq111
我们通常会认为,在html中,位置靠前的js,会优先加载、执行。这句话大部分情况下正确,但是随着浏览器的升级调整,新特性的加入,这个加载、执行顺序也越来越复杂。
首先我们看一个最简单的情况
<script src="a.js"></script><script src="b.js"></script>chrome 下,为了减少等待时间,a,b 会并行加载,但是执行时则会先 a 后 b;并不是 a 加载,a 执行,b 加载,b 执行的顺序。
async、defer 的加入
关于 async、defer 功能及异同的介绍:点击前往
- async 属性会让 js 并行加载,并在 js 加载完成后立即执行,也就是说执行顺序由加载速度定,而不是 html 中的先后顺序
- defer 属性 js 同样会并行加载,而执行时间点为文档解析完成后,按照 html 中的顺序执行,也就是说 defer 不影响执行顺序
所以如果代码是这样的
<script src="a.js" async></script><script src="b.js"></script>a,b 同样并行加载,但此时如果是 b 先加载完,则 b 会优先执行;
动态插入脚本的执行顺序
例:
<script type="text/javascript"> var snode = document.createElement('script') snode.src = 'a.js' document.head.appendChild(snode)</script><script type="text/javascript"> var snodeb = document.createElement('script') snodeb.src = 'b.js' document.head.appendChild(snodeb)</script>对于支持 async 属性的浏览器,动态插入的外链脚本, 相当于默认具有 async=true;也就是 a.js, b.js 执行顺序不确定的;
如果需要让动态插入的脚本按插入顺序执行,可以显式设置 async = false;
<script type="text/javascript"> var snode = document.createElement('script') snode.src = 'a.js' snode.async = false document.head.appendChild(snode)</script><script type="text/javascript"> var snodeb = document.createElement('script') snodeb.src = 'b.js' snodeb.async = false document.head.appendChild(snodeb)</script>动态插入内联脚本
特殊场景下,我们会使用 js 动态往页面插入脚本,比如:
<script type="text/javascript"> var dn = document.createElement('script') dn.text = 'console.log("dn")' document.head.appendChild(dn)</script>此动态插入的脚本并不会执行;
但是如果我们换一种方式,将其改成外链,但是 url 用一个 data url 来代替,则可以执行,比如:
<script type="text/javascript"> var dn = document.createElement('script') dn.src = 'data:application/javascript,' + 'console.log("dn")' document.head.appendChild(dn)</script>脚本的执行与其 onload 事件执行的先后
例:
<script type="text/javascript"> function track(evt) { console.log(evt.type + ' : ' + evt.target.src) }</script><script src="c.js" onload="track(event)"></script>执行后可以看出 js 加载完成后会优先完成解析,执行,然后才触发 onload 事件。内联脚本没有 onload 事件触发。如果我们要动态加载一个脚本,并在其加载执行完成后有回调,onload 就可以派上用场了。