Skip to main content

CRP: Critical Rendering Path

关键渲染路径

注意

  • 阻塞解析阻塞渲染 是不同的!

关于渲染过程

build DOM -> fetch CSS/fetch JS -> build CSSOM/run JS -> build DOM -> render tree -> layout -> paint

注意:这样的过程并不是严格按照顺序一次性执行完毕的,现代浏览器会边解析边渲染。因此要提高首次渲染速度的关键是,尽早解析更多的DOM,尽早加载解析更多的CSSOM,延迟脚本代码的执行延迟前述两个过程

在非异步渲染(同步渲染)情况下:

  • html的加载与解析是阻塞渲染的
  • 默认情况下,css的加载与解析是阻塞DOM解析与渲染的(与媒体查询/媒体类型声明有关)(因此需要尽早下载解析以完成首次渲染,所以一般将css样式表放在最前,防止页面发生样式跳动)
  • js的加载与解析可能是阻塞渲染(DOM构建)的(与标签声明有关)
  • js脚本的执行会阻塞后续的DOM的解析,而前面解析完成的DOM会立即渲染出来,所以一般将js脚本放在最后,以免影响DOM的解析
  • 外部样式表/脚本的远程获取是阻塞渲染的
  • Font加载阻塞应用到该字体部分的内容渲染,为了避免FOUT(Flash Of Unstyled Text),但是会造成FOIT(Flash Of Invisible Text)问题。只有当字体超过一段时间仍未加载成功时,浏览器才会降级使用系统字体。每个浏览器都规定了自己的超时时间
  • 网页其他资源如图像视频等不是阻塞渲染的
  • 多个外部的js/css资源可以异步的请求获取,但是解析过程不是并行的
  • 外联样式的载入与解析会阻塞JS脚本的执行与DOMContentLoaded事件的触发。 DOM的构建依赖js脚本的执行完成(js脚本的加载执行会阻塞DOM的构建),js的执行依赖脚本所处之前的样式表CSSOM构建完成(CSSOM在js执行前构建完成,只要浏览器遇到 script 标记,就会进行阻止,并等到 CSSOM 构建完毕)。这样的结果是:js可以读取修改DOM/CSSOM的属性,开发者可以在脚本中获取到DOM的样式、位置等信息
  • 但是DOM的构建并不会受到CSS的直接影响,只有在存在脚本的情况下,才会先等待CSSOM构建完成,再执行脚本,再继续构建DOM。如果没有脚本,DOM会直接构建完成,触发DOMContentLoaded,再构建CSSOM

在异步渲染的情况下:

  • 动态插入的 外联样式或脚本不阻塞 DOM的解析/渲染
  • 动态插入的 内联样式或脚本会阻塞 DOM的解析/渲染
  • 未连接到DOM的样式表或脚本(创建了link或script标签但尚未append到DOM结构中)不会被下载/解析/执行
  • 动态插入的样式或脚本可以监听onload/onerror事件

关于async defer normal \<script> 标签

CRP流程

CRP流程 CRP流程

CRP过程中的关键时间点

时间点

  • domInteractive:DOM刚刚解析完成构建完成的时间点。performanceTiming.domInteractive返回这个时间点的时间戳
  • DOMContentLoaded:当DOM构建完成,并且其所属script之前的样式表加载解析CSSOM完成时,触发DOMContentLoaded事件
  • load: 所有资源加载解析完成时触发

优化CRP

最大限度缩短执行渲染过程到paint耗费的总时间

如前述 尽早解析更多的DOM,尽早加载解析更多的CSSOM,延迟脚本代码的执行延迟前述两个过程

“优化关键渲染路径”在很大程度上是指了解和优化 HTML、CSS 和 JavaScript 之间的依赖关系谱。

  • 减少脚本文件数量。可以将多个文件进行打包合并,一方面减少了请求的数量,一方面减少了 等待加载-执行 的重复过程的次数

  • 尽量保持css层级的扁平简单,尽量使用class/id(加快节点查找),以简化CSSOM构建过程,加快构建速度

  • 将脚本置于文件末尾,或者使用defer属性。这样不会因为脚本执行延迟DOM/CSSOM的构建,不会延迟图片等非关键资源加载。chrome会先执行defer再触发DOMContentLoaded事件

  • 使用async属性,这样不会因为远程加载过程阻塞渲染。

  • 尽早加载css文件

  • 尽量不要使用@import指令导入样式,这样的导入不会并行的加载文件,而是等待样式文件加载完成才会import

  • 对样式表使用媒体类型命令/媒体查询条件,只加载关键资源

  • 对样式表文件使用rel="preload",提前加载资源(但不会立即构建CSSOM),在未来中使用

  • 通过js动态加载样式表,这样不会阻塞首次渲染

  • 使用web worker处理耗时js逻辑

参考

https://segmentfault.com/a/1190000008984446

官方开发者文档

优化关键渲染路径

CSS/JS 阻塞 DOM 解析和渲染

异步渲染的下载和阻塞行为