Skip to content

script元素和页面解析的关系

script元素的处理

  • 浏览器在解析HTML的过程中,遇到了script元素是不能继续构建DOM树的
  • 它会停止继续构建,首先下载js代码,并且执行js脚本
  • 只有等到js脚本执行结束后,才会继续解析HTML,构建DOM树

为什么要这样做呢?

  • 因为js的作用之一就是操作dom,并且可以修改dom
  • 如果我们等得到dom树构建完成并且渲染再执行js,会造成严重的回流和重绘,影响页面的性能
  • 所以再遇到script元素时,优先下载和执行js代码,再继续构建dom树

但是这个往往也会带来新的问题,特别是现代页面开发中:

  • 在目前的开发模式中(比如Vue,React),脚本往往比HTML页面更“重”,处理时间需要更长
  • 所以会造成页面的解析阻塞,在脚本下载、执行完成之前,用户在页面上什么都看不到

为了解决这个问题,script元素给我们提供了两个属性(attribute):defer和async

defer属性

defer属性告诉浏览器不要等待脚本下载,而继续解析html,构建dom tree

  • 脚本会由浏览器来进行下载,但是不会阻塞dom tree的构建过程

  • 如果脚本提前下载好了,它会等待dom tree构建完成,在DOMContentLoaded事件之前先执行defer中的代码

  • 在defer代码中,dom tree已经构建完成了,可以操作dom

  • 所以DOMConteneLoaded总是会等待defer中的代码先执行完成

  • 另外多个带defer的脚本是可以保持正确的顺序执行的

  • 从某种角度来说,defer可以提高页面的性能,并且推荐放到head元素中

    • 这样可以让浏览器提前去下载,而不是等到都加载完了然后再去下载

注意:defer仅适用于外部脚本,对于script默认内容会被忽略

async属性

async特性与defer有些类似,它也能够让脚本不阻塞页面

async是让一个脚本完全独立的:

  • 浏览器不会因async脚本而阻塞(与defer类似)
  • async脚本不能保证顺序,它是独立下载、独立运行,不会等待其他脚本
  • async不能保证在DOMContentLoaded之前或之后执行
  • 它是下载一旦好了就执行,不能确定脚本运行顺序

特点:

  • 比较危险,不能在里面随便操作DOM,因为不知道有没有构建好

总结defer和async

  • defer通常用于需要在文档解析后操作DOM的js代码,并且对多个script文件有顺序要求的

  • async通常用于独立的脚本,对其他脚本,甚至DOM没有依赖的