今天群里有人问为何会出现脚本的加载顺序与定义脚本顺序不一致的问题,这个问题引发了个人好奇,通过一番调研,有了这篇文章。html
首先,W3C 推荐 script
脚本应该被当即加载和执行,其次,通过网络搜索,我只发现了 1 例相同的问题,因此这个问题的真伪其实还有待进一步验证,可是从逻辑上说,浏览器会并行加载静态资源,对于 Chrome,能够并行加载 6 个资源,若是其中一个资源获取的比较缓慢,那么会影响串行的下 6 个请求的发送,若是可以预先测试出 6 个通畅的请求,一并发送,那么就能够提高网络加载的总体性能。但浏览器是否有这一层优化呢?目前我只见到这篇文章提到过浏览器彷佛有这个优化算法,可是并无在其余地方获得确认。算法
咱们有两种方式使用 <script>
标签:浏览器
src
属性引入外部 JavaScript 静态资源;<script>
开闭标签内的 JavaScript 脚本;但其实本质上这两种方式是一回事,其最终的目的就是让浏览器在当前页面执行 JavaScript 脚本,只不过对于前者而言多了一道工序:将服务器返回的 JavaScript 脚本内容插入 <script>
标签内部,而后在执行它。缓存
所以,对于 <script>
标签,咱们惟一关心的只有一点:JavaScript 脚本被执行的时机。服务器
在页面中,咱们有两处地方能够放置 <script>
标签:网络
<head> ... </head>
head 标签内部;<body> ... </body>
body 标签内部;在 <head>
标签中插入引用外部 JavaScript 会致使 <body>
标签内的内容在 JavaScript 被彻底下载,解析,执行完毕后才会被解析,这期间用户会看到浏览器一片空白,所以会影响用户体验。(这是因为浏览器从上至下解析 HTML 文档,而 JavaScript 的下载,解析和执行会停止浏览器的解析过程)。并发
所以业界通行的作法是,将 script
标签放置 <body>
底部,从而避免 JavaScript 阻塞页面渲染。异步
但不管如何,咱们的 JS 脚本的执行顺序是相同的:根据其在页面中的位置决定前后顺序。async
可是咱们能够经过两个属性改变这一顺序。性能
async
属性是 HTML5 规范新推出的一个属性,用来告知浏览器应该尽量的异步加载脚本。全部的浏览器都支持
该属性。具备该属性的脚本咱们既没法得知它下载的时间,也没法得知它执行的时机,咱们惟一知道的只有两点:
⚠️ 注意,script
标签必须有 src
属性,且属性值有效。
defer
属性向浏览器指明了脚本被执行的时机:“文档解析以后,DOMContentLoaded
事件被触发以前(即 HTML 文档被彻底加载和解析,无论样式表,图片或 iframe 是否加载完毕。恩,一个很微妙的时间 🤔)”,这里须要注意的是,并非具备 defer
属性的脚本会等待 DOMContentLoaded
的触发,并赶在这以前执行,而是具备 defer
属性的脚本会延迟 DOMContentLoaded
事件的触发。
理论上讲全部带有 defer
属性的脚本会按照在 HTML 中定义的顺序被依次触发,但遗憾的是实际中好像并不会这样(此处有待作实验进一步验证)。
特别须要注意的是,带有 defer
关键字的脚本也是以异步的形式被加载的。
⚠️ 注意,script
标签必须有 src
属性,且属性值有效。
让咱们总结一下,<script>
脚本默认被浏览器以必定顺序并行下载,并按照定义的顺序依次执行(在这期间,加载好的代码安静的待在浏览器缓存中,直到全部前置的脚本被加载和解析完成)。
咱们有两种方式更改 <script>
的下载和执行时机,经过属性 async
和 defer
,这两个属性都会采用异步的方式下载脚本,而他们的区别在于:添加了 async
属性的脚本会被浏览器异步加载,但咱们没法得知其被下载和执行的时间,而添加了 defer
属性的脚本将会在文档解析后,DOMContentLoaded
事件触发前被执行。
最后,让咱们再想一想这两个属性的使用时机:
async
:因为咱们没法知道添加了 async
属性脚本的具体下载,执行时间,所以全部具备依赖关系或操做 DOM 元素的脚本都不适宜添加该属性,反过来讲,任何不具有依赖关系,不操做 DOM 的脚本均可以添加该属性以节约脚本的下载时间,什么类型的脚本知足上述要求呢?我能想到的有埋点类脚本,例如访问统计脚本,广告流量统计脚本等;defer
:该关键字给咱们的脚本一个完美的下载,执行时机,在 DOM 准备好以后,要不是现实中并不是依次加载带有 defer
属性的脚本,我几乎就要建议你给全部脚本添加该属性了,它既能够保障咱们的脚本被异步加载,又可使咱们得到 DOM 操做的肯定性,除了内联脚本,应该给每一个没有依赖的脚本都添加该属性。