前端固然要从 HTML 开始,今天来聊聊在 script 标签中加上 async/defer 时的功能及差别。html
咱们都知道,浏览器解析 HTML 是一行一行按照顺序向后读取的,在传统的写法中,当浏览器读到 <script> 时,便会暂停解析 DOM,同时当即开始下载 <script> 中定义的资源,并在下载完成后马上执行。因为这样的特性,可能会形成 DOM 树在尚未彻底解析时就开始执行 JavaScript,须要操做 DOM 的程序可能所以没法正确执行,从而形成许多问题;或是因为 <script> 中的资源下载、执行时间过程,用户会卡在白画面,并会产生以为网站太慢很差用之类的体验。前端
而解决方法也很简单,咱们须要把 <script> 标签的位置都放到 <body> 的最后一行来避免 DOM 树解析不彻底的问题,可是在复杂的网站中, HTML、JavaScript 的个头都很大,须要等到整个 DOM 树都载入完成才开始下载 <script> 内的资源,从网站读取完成到可操做,会产生明显的延迟感。webpack
那这种问题该怎么解决呢?git
从HTML4 开始,<script> 多了 defer 属性,而 HTML5 则多了 async,二者都是用来帮助开发者控制 <script> 内资源的载入及执行顺序,以及避免 DOM 的解析被资源下载卡住的。github
defer 的意思是延迟(Deferred),在 HTML4.01 规范 中规定:web
❝ 设置后,这个布尔属性会向用户代理提示该脚本将不会生成任何网页内容(例如,JavaScript中不会生成 “document.write”),所以,用户代理能够继续解析和渲染。 ❞
也就是说,在加上 defer 属性后,浏览器会继续解析、渲染画面,而不会由于须要载入<script> 内的资源而卡住;实际执行时,会在 DOMContentLoaded 执行以前,由上到下的依照摆放顺序触发。浏览器
听起来很方便对吧?但要提醒各位,虽然 W3C 规范上说 defer 属性会是一个布尔值,但 IE9 之前的版本是自定义的,即便写成 <script defer="false"> 仍然会有 defer 的效果,使用时要特别注意。app
又是你这个老不死的 IE……框架
async 的意思是异步(Asynchronous),在 HTML5 规范 中规定:异步
❝ …若是存在 async 属性,则脚本将会在可用时当即异步执行 … ❞
在 <script> 标签中加上 async 属性后,与defer 的相同点是也会在后台执行下载,但不一样的是当下载完成会立刻暂停 DOM 解析(若是尚未解析完成的话),并开始执行 JavaScript。由于下载完成后会当即执行,加上 async 属性后,就没法保证执行顺序了。
这个属性在标准中,同时也支持经过 JavaScript 动态插入 <script> 的状况。例如:
const script = document.createElement('script') script.src = "/something/awesome.js" document.body.append(script)
动态建立的 <script>,默认就是异步载入;但能够经过设定属性将它关闭:
script.async = false
在主流的现代浏览器中,<script> 的属性能够加上 type="module"。这时浏览器会认为这个文件是一个JavaScript 模块,其中的解析规则、执行环境会略有不一样;这时 <script> 的默认行为会像是 defer 同样,在后台下载,而且等待 DOM 解析、渲染完成以后才会执行,因此 defer 属性没法在 type="module" 的状况下发生做用。但一样能够经过 async 属性使它在下载完成后即刻执行。
如今你应该明白这两个属性的特色了,那么该怎样正确地使用呢?
defer 因为后台载入、不打断渲染及确保执行顺序的特色,基本上在没特殊需求的状况下,在 <script> 中设置一下就好了;固然 <script> 自己的摆放顺序仍是要稍微留心一下。
async 比较特别,由于在下载后会马上执行,且不保证执行顺序,通常常见的应用是设定在彻底独立的小小模块中,例如背景Logo、页面广告等,在避免形成使用者体验变差的同时,尽可能早的产生效果。
如今前端开发大都经过 Webpack 等打包工具来辅助处理,不多有本身设定这些属性的机会;开发者能够经过 [script-ext-html-webpack-plugin](https://github.com/ numical/script-ext-html-webpack-plugin) 等插件的帮助,将切分好的 Chunk 设定个别须要的 <script> 属性。
async 及 defer 是 <script> 专属的属性,对于网页中的其余资源,能够经过 <link> 的preload、prefetch 属性,来帮咱们延迟加载 将来才须要用到的资源。
虽然 <script> 的async、defer 这些属性的设置大都已经包含在现代框架的打包流程中了,但只有扎实的认识这些网页最基础的规范,才能明白本身写出来的代码最后会产生什么效果。