js脚本一定会阻塞浏览器渲染文档吗?script标签的async和defer属性

js脚本一定会阻塞浏览器渲染文档吗?script标签的async和defer属性

一、浏览器解析器遇到script脚本的解析逻辑

1.情形一:页面中引入的script脚本会阻塞浏览器解析渲染文档。

浏览器解析文档时,默认是按照排列顺序向下解析的,当遇到script标签时,和其他标签元素一样(例如一个div),会先解析该元素(脚本),解析完成后再继续向下走完成剩余文档的解析和渲染。也就是说默认情况下,script脚本会阻塞文档的解析渲染。

注意,如果我们的script脚本是放在页面底部的内联脚本,那么它对文档的解析渲染,在结果上影响不大。

但如果script脚本是外部脚本(通过网址引入的那种),那么这个脚本需要下载和解析执行,这期间会阻塞浏览器对文档的向下解析渲染,直至脚本下载执行完成,才会继续向下解析渲染。如果这个脚本出错了,可能还会导致整个页面永远无法正常渲染呈现。

注意,DOM树的生成是受JavaScript代码执行影响的,JavaScript代码会“阻塞”页面UI的渲染。

2.情形二:页面中引入的script脚本不会阻塞浏览器解析渲染文档。

情形一中提到的脚本执行的同步和阻塞的情形,是指默认情况下的script脚本加载方式。script标签有两个属性,一个是defer(翻译为延迟),二是asyn(翻译为异步,没错,和我们的常见的ajax和axios的异步是一个意思),这两个属性都可以改变脚本的加载执行方式(在浏览器支持的情况下,这两的兼容性,后面会补充)。

延迟:延迟是指当浏览器解析到script脚本时,会继续向下载入和解析文档,等到文档载入解析完成且可以操作文档时,才开始执行脚本。

异步:异步是指当浏览器解析到script脚本时,会立刻下载和执行脚本,但同时浏览器解析器也会继续向下解析渲染文档,不会造成阻塞的现象。这使得脚本可以尽快的被载入执行,这很想我们前端调用post异步接口,并不影响文档的正常解析渲染。

ps:defer和async属性像是在告诉浏览器,链接进来的脚本不会使用document.write(),也不会生成文档内容,因此浏览器在下载脚本时可以继续解析和渲染文档。

值得注意的是,使用defer属性的脚本,当有多个的时候,这些脚本会按照他们在文档中排列的顺序载入执行。而使用asyn属性的脚本,当有多个的时候,会顺序开始触发载入,但谁先完成载入就谁先执行,这就是说,他们的执行顺序可能是无序不确定的。在有的时候知道这点很重要,因为这可能涉及一些有强制顺序的逻辑处理。

二、script脚本标签的两个特殊属性:async与defer

注意事项

1.仅仅IE浏览器支持defer属性,而async属性则被ie10+和其他现代浏览器支持。

2.async属性是 HTML5中的新属性,仅适用于外部脚本(即是在script标签使用src属性时)。

外部脚本执行情况汇总

  • 一个script标签同时使用了async和defer,则执行async,忽略defer。
  • 一个script标签只使用defer,且 defer="defer",则脚本将在页面完成解析时执行。
  • async和defer都不使用,遇到script脚本即会马上载入和执行脚本,此时会阻塞页面继续向下解析渲染。
  • 只使用async且async="async",则脚本相对于页面的其余部分异步地执行。