关键渲染路径CRP笔记

关键渲染路径(Critical Render Process)是浏览器将HTML、CSS和JavaScript代码转换为屏幕上像素的步骤序列,它包含了DOM(Document Object Model)、CSSOM(CSS Object Model)、渲染树(Render Tree)和布局(Layout)。

浏览器的一次展示页面的过程从下载并解析HTML开始,将HTML解析为DOM,HTML内的<script>标签可以外联JavaScript,JavaScript会反过来更改DOM。HTML内的<link>标签又可以外联CSS,浏览器可以将CSS解析为CSSOM。浏览器引擎将DOM与CSSOM解析创建渲染树。之后与屏幕尺寸与像素有关的布局将被确定。最后浏览器的绘制引擎将会把布局绘制到页面上。

FOUC(Flash Of Unstyled Content)问题就是因为关键渲染路径某一步出问题导致的

关键渲染路径具体流程

浏览器使用渲染引擎或排版引擎(Browser Engine)来布局渲染以及DOM生成,常见渲染引擎有Safari、Chrome使用WebKit内的WebCore、Chrome除iOS端、Opera都在用的Blink(谷歌自行开发的WebCore分支)、Firefox用的Gecko。

Webkit集成了渲染引擎WebCore与JavaScript引擎JavaScirptCore,而Chrome中使用的Blink则仅为渲染引擎,V8为其JavaScript引擎。Firefox使用Gecko做渲染引擎,SpiderMonkey为其JavaScript引擎。

构建DOM树

浏览器接受HTML并处理为DOM的过程:Bytes → Characters → Tokens → Nodes → DOM。DOM树包含所有HTML标签包括注释与display:none的内容,因为这一步还未关联到CSSOM上,这一步也包括JavaScript动态添加的内容。

当建立DOM时如果遇到了<script>标签,由于JavaScript有可能会操作DOM树,所以构建DOM树的过程会分为以下三种相应方式

  • 默认情况下DOM解析会停止,等待JavaScript完成下载(内联不需要)与执行然后继续解析

  • <script>标签上有defer属性,立即并行开始下载(运行环境负责并行任务,不影响DOM树解析),但是JavaScript的执行延迟到DOM树解析完后,DOMContentLoaded之前(该事件原本在DOM树解析完成之后但事件会被推迟到代码执行之后),并且具有defer标签的JS会按script出现顺序顺序执行

  • <script>标签上有async属性,立即并行开始下载,下载完后停止DOM解析并执行JavaScript,执行顺序不一定按照script出现顺序来

当构建DOM时遇到了CSS,CSS会被在另外的线程被下载执行,正常情况下不会影响DOM处理与JavaScript下载,但是任何JavaScript代码的执行必须在CSS下载与CSSOM构建完成之后,后面的渲染流程也必须在DOM和CSSOM都准备好之后再进行

构建CSSOM

浏览器处理CSS过程与处理HTML过程类似:Bytes → Characters → Tokens → Nodes → CSSOM,CSSOM(CSS Object Model)不包含不会被显示在页面上的HTML节点例如<meta><script><title>等等。

只有DOM和CSSOM构建完毕后浏览器才会渲染元素,所以CSSOM的构建也会影响首屏时间。

渲染树构建

渲染树(Render Tree)包含了内容和样式也即是DOM和CSSOM,两者结合为渲染树,浏览器会从DOM树的根节点开始检查哪些CSS规则被添加,如果元素CSSOM上有display:none则它本身和其后代都不会出现在渲染树中,渲染树中只包含可见元素。

布局Layout

布局(回流)在Chrome、Opera、Safari和Internet Explorer中被叫做Layout,在Firefox中叫做Reflow

布局决定了在哪里与如何在页面上放置元素,决定每个元素的宽和高,它取决于屏幕尺寸和像素数量。在首次布局完毕之后还有可能再次布局,当网站是响应式且用户改变浏览界面宽度比如手机的横屏竖屏调整时,或节点添加改变内容或者更新节点的盒模型时还会发生。

布局受DOM节点数量的影响,且布局操作会阻塞浏览器的其他操作,尽量减少布局次数

当执行以下操作时布局(Layout / Reflow)将会发生:

  • 插入、删除、更新DOM元素
  • 更改页面内容,例如修改Input中的文字
  • 移动DOM元素
  • DOM元素执行动画
  • 更改CSS样式
  • 更改一个元素的类名class
  • 更改窗口大小
  • 滚动相关的属性与计算
    • elem.scrollBy(), elem.scrollTo()
    • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
    • elem.scrollWidth, elem.scrollHeight
    • elem.scrollLeft, elem.scrollTop,设置它们的值,同样会影响
  • 获取下列盒模型属性与计算时
    • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent
    • elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight
    • elem.getClientRects(), elem.getBoundingClientRect()
  • 聚焦方法elem.focus()会导致两次强制布局(回流)
  • 其他的
    • elem.computedRole, elem.computedName
    • elem.innerText
  • window.getComputedStyle()
    1. 要获取的元素在shadow DOM
    2. 使用了media queires
      • min-width, min-height, max-width, max-height, width, height
      • aspect-ratio, min-aspect-ratio, max-aspect-ratio
      • device-pixel-ratio, resolution, orientation
    3. 获取以下某一种属性
      • height, width
      • top, right, bottom, left
      • margin [-top, -right, -bottom, -left, 或简写] ,仅当 margin 是固定值。
      • padding [-top, -right, -bottom, -left, 或简写] ,仅当 padding 是固定值。
      • transform, transform-origin, perspective-origin
      • translate, rotate, scale
      • webkit-filter, backdrop-filter
      • motion-path, motion-offset, motion-rotation
      • x, y, rx, ry
  • Window相关
    • window.scrollX, window.scrollY
    • window.innerHeight, window.innerWidth
    • window.getMatchedCSSRules() 仅重新计算样式
  • 表单
    • inputElem.focus()
    • inputElem.select(), textareaElem.select()
  • 鼠标事件
    • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY
  • Document
    • doc.scrollingElement 仅重新计算样式
  • Range
    • range.getClientRects(), range.getBoundingClientRect()
  • SVG
  • 内容操作

更多的操作可以搜索Chromium的源码,关键词updateLayoutIgnorePendingStylesheets、updateLayoutTree

绘制Paint

绘制是将布局好的元素内容(例如边框、背景颜色、阴影、文本等)转化为屏幕上的像素的过程,绘制操作也被称为格栅化操作(Rasterization),浏览器在这一步也会为布局创建层Layer,绘制操作在浏览器的多个线程并行。

绘制操作以往是使用CPU来进行的但是这样会比较慢,现代浏览器支持使用GPU来进行绘制操作速度快。比较Chromium中软件和GPU绘制操作

绘制为每层的绘制,浏览器实际关键渲染路径最后还有一步合成(Composite),用于正确的将多层画面重叠显示在屏幕上

关键渲染路径性能分析

工具

Google提供了Lighthouse网站与Lighthouse NPM包分析工具用来对网站进行性能与可访问性的测试,能快速的测试、循环访问和分析CRP性能。

浏览器提供了dev tools调试工具,在Chrome Dev Tools的Network和Performance组件中可以分析页面性能

NodeJS提供了Inspector API,通过它我们可以用JavaScript代码与V8引擎的Inspector进行交互,可以分析CPU与堆的使用情况,Chrome Dev Tools实现了Inspector协议

浏览器提供了Navigation Timing API(IE与Safari不支持),通过这个API和页面加载时浏览器发出的其他事件可以捕获并记录任何页面的实际CRP性能

  • domLoading:浏览器即将开始解析第一批收到的HTML文档字节
  • domInteractive:DOM准备就绪,浏览器完成对所有HTML的解析并且DOM构建完成
  • domContentLoaded:DOM和CSSOM均准备就绪,也就是DOM已准备就绪并且没有样式表阻止JavaScript的执行,现在可以构建渲染树了,大部分JavaScript引擎在该事件发生之后才开始执行JavaScript逻辑所以下方的两个时间戳可以帮我们记录花费的时间
    • EventStart
    • EventEnd
  • domComplete:所有处理完成并且网页上所有资源比如图像也已下载完毕,浏览器加载状态结束
  • loadEvent:浏览器触发onload的事件触发额外的应用逻辑
    • Start
    • End

关键渲染路径优化

像素管道

像素管道是一个对页面渲染每个像素的处理流程的一种抽象化描述,像素管道包含以下几个关键节点

  • JavaScript:JavaScript实现的视觉效果,修改DOM、CSS Animations、Transitions、Web Animation API
  • Style:计算CSS应用范围,具体标签选择器对应DOM节点
  • Layout:计算布局,当页面元素位置或页面宽度元素宽度发生变化时会发生回流Reflow其实就是布局的重新计算
  • Paint:填充像素,它涉及绘出文本、颜色、图像、边框和阴影,基本包括元素的每个可视部分,绘制是分布在不同层Layor上的
  • Composite:合成,由于页面可能会分布在多层,合成的目的就是将所有的层按照顺序渲染出来

不是所有帧都会经过整个像素管道的处理,一般有三种情况:

  1. JS/CSS → Style → Layout → Paint → Composite

    如果修改元素的Layout属性,也就是改变了元素的几何属性例如宽度、高度、左侧或顶部位置、字体大小、增删DOM元素、浏览器大小变化、盒模型变化等,那么浏览器将必须检查所有的属性然后重排页面布局称作回流Reflow。发生回流的耗时和占用资源是最多的,因为浏览器会从html这个root frame开始向下遍历依次计算所有节点的几何尺寸和位置,因为一个元素可能影响整个页面的布局。

  2. JS/CSS → Style → Paint → Composite

    如果只修改元素的Paint Only属性例如背景图片、文字颜色或阴影等,即不会影响页面布局的属性,浏览器将跳过布局,但是依然会执行绘制,称作重绘Repaint。回流Reflow(改变了Layout)之后也会重绘Repaint

  3. JS/CSS → Style → Composite

    如果更改了既不会导致回流,也不会造成重绘的操作,就会只进行样式计算和布局和合成操作,这种操作最轻量化,可以用在需要页面高性能的环境下例如动画或滚动场景。一个最重要的例子就是transformperspective、CSS样式的改变在Blink(Chrome的渲染引擎)和Gecko(Firefox的渲染引擎)中只会触发合成操作,开支非常小。

CSS Triggers网站提供了在这些CSS变化时会进行的以上三个流程的哪一个

优化建议

  • transformperspective做形变和位移减少reflow
  • 避免逐个修改节点样式尽量一次性修改
  • 使用DocumentFragment需要多次追加元素或修改的内容缓存起来,最后一次性挂载到DOM上
  • 需要多次修改的元素可以使用display:none先从DOM节点上移出,操作完再标记display:block放回DOM上,因为不在DOM上的元素修改不会导致重绘
  • 避免多次读取某些属性
  • 使用Flexbox可以显著缩短布局耗费的时间
  • 通过绝对定位将复杂的节点元素脱离文档流,形成新的Layer,降低回流成本

渲染优化建议

  • 样式文件应该在head尽快下载执行(CSS下载解析在独立线程中不影响DOM下载解析)、JavaScript放在body结束前(下载解析会阻塞DOM解析且JS有可能修改DOM元素,所以等到DOM内容基本完成解析再下载执行)
  • 优化CSS选择器,尽量减少层级嵌套,减少子选择等
  • DOM的读写操作应该放在一起,读读读写写写但是不要读写读写读写
  • 不要一条一条改变样式,尽量放到一个类中,或将元素脱离文档再修改样式
  • positionabsolutefixed的元素重排开销较小,

总结

渲染流程

  1. 浏览器引擎边下载HTML边构建DOM树,以User Agent Stylesheet(浏览器内置样式)为原料构建CSSOM树
  2. 如果HTML解析到<script>
    • 默认下载与执行都阻塞DOM树的构建,inline的就阻塞并执行
    • defer开另外线程下载但推迟执行到DOM树构建完毕且在DOMContentLoaded之前
    • async开启另外线程下载但下载完立即阻塞DOM树构建并执行
  3. 如果HTML解析到CSS,不论inline内联、internal元素上还是外联的CSS(外联的需要下载完成之后)刷新CSSOM树,这个过程与DOM树生成是在不同线程不会阻塞DOM的生成
    • CSSOM中不会出现不可见的元素,包括display:none的元素以及其子元素,但visibility:hiddenopacity:0的元素还是会被添加到CSSOM上
  4. 等到DOM树完成生成(JavaScript也执行完毕)、CSSOM树构建完成之后执行布局
  5. 布局完成之后执行绘制操作

性能优化

根据上方优化性能的操作编写代码与执行DOM操作

可能阻塞CRP的情况

HTML

如果HTML本身加载就很慢,就不用谈其他的了

CSS

CSS下载解析阻塞渲染树的创建与JavaScript的执行,但动态插入的外链CSS不会阻塞页面渲染,动态插入的内联CSS不会阻塞DOM的解析和渲染

JavaScript

同步的JavaScript会阻塞DOM的解析和渲染,异步的JavaScriptdeferasync在下载时都不会影响DOM的生成,但async会在下载完成之后执行时阻塞DOM生成,动态插入的外链JavaScript脚本不会阻塞DOM解析生成

CRP关键渲染路径笔记的更多相关文章

  1. 优化关键渲染路径CRP

    什么是关键渲染路径? 从收到 HTML.CSS 和 JavaScript 字节到对其进行必需的处理,从而将它们转变成渲染的像素这一过程中有一些中间步骤 浏览器渲染页面前需要先构建 DOM 和 CSSO ...

  2. 基于Webkit的浏览器关键渲染路径介绍

    关键渲染路径概念 浏览器是如何将HTML.JS.CSS.image等资源渲染成可视化的页面的呢?本文简单介绍一下渲染过程中涉及到的关键步骤. 该过程分为四步:模型对象的构建.渲染树构建.布局.绘制. ...

  3. [Unity Shader笔记]渲染路径--Forward渲染路径

    [Unity Shader笔记]渲染路径--Forward渲染路径 (2014-04-22 20:08:25) 转载▼ 标签: shader unity renderingpath forward 游 ...

  4. Unity Lighting - Choosing a Rendering Path 选择渲染路径(三)

      Choosing a Rendering Path 选择渲染路径 Unity supports a number of rendering techniques, or ‘paths’. An i ...

  5. 离屏渲染学习笔记 /iOS圆角性能问题

    离屏渲染学习笔记 一.概念理解 OpenGL中,GPU屏幕渲染有以下两种方式: On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行. O ...

  6. Unity3D光照前置知识——Rendering Paths(渲染路径)及LightMode(光照模式)译解

    简述 Unity supports different Rendering Paths. You should choose which one you use depending on your g ...

  7. 前向渲染路径细节 Forward Rendering Path Details

    正向渲染路径细节 Forward Rendering Path Details Forward Rendering path renders each object in one or more pa ...

  8. RenderingPath 渲染路径

    http://blog.csdn.net/lichaoguan/article/details/42554821 RenderingPath 渲染路径 Deferred Lighting 延时光照 延 ...

  9. 渲染路径-Deferred Lighting 延时光照

    http://blog.csdn.net/heyuchang666/article/details/51564954 注意: 最后3个步骤注意下 延时光照是有着最高保真度的光照和阴影的渲染路径.如果你 ...

  10. 渲染路径-u3d渲染路径比较

    Unity支持不同的渲染路径.应具体取决于你的游戏内容和目标平台/硬件来选择使用哪一个.不同的渲染路径有不同的特点和性能特点,主要影响灯光和阴影.        项目所使用的渲染路径在Player S ...

随机推荐

  1. 开源数据库PolarDB为什么能捕获娃哈哈的心?

    简介: 在10月25日由阿里云开发者社区.PolarDB开源社区.infoQ联合举办的「开源人说」第三期--<数据库PolarDB专场>沙龙上,中启乘数科技(杭州)有限公司联合创始人唐成带 ...

  2. 用积木讲运维,这样的IT人太会了

    简介: 日志服务SLS提供数据采集.加工.分析.告警可视化与投递功能,为AIOps.大数据分析.运营服务.大数据安全等场景提供支撑,并能以搭积木的方式适配各类运维场景,辅助企业的IT决策.近日,日志服 ...

  3. Go Mysql Driver 集成 Seata-Golang 解决分布式事务问题

    简介: 2020 年 4 月,我们开始尝试实现 go 语言的分布式事务框架 Seata-Golang.众所周知,Seata AT 模式以无业务代码侵入的特点,被广大开发者推崇.Java 版 Seata ...

  4. 基于Delta lake、Hudi格式的湖仓一体方案

    ​简介: Delta Lake 和 Hudi 是流行的开放格式的存储层,为数据湖同时提供流式和批处理的操作,这允许我们在数据湖上直接运行 BI 等应用,让数据分析师可以即时查询新的实时数据,从而对您的 ...

  5. 设置Mysql数据库允许远程连接

    Mysql数据库用户权限设置 1.进入容器 docker exec -it mysql_test /bin/bash 注意:由于我是通过docker安装的数据库,所以在操作之前需要进入容器,直接安装在 ...

  6. 解密Prompt系列28. LLM Agent之金融领域摸索:FinMem & FinAgent

    本章介绍金融领域大模型智能体,并梳理金融LLM的相关资源.金融领域的大模型智能体当前集中在个股交易决策这个相对简单的场景,不需要考虑多资产组合的复杂场景.交易决策被简化成市场上各个信息,包括技术面,消 ...

  7. PCI-E与SATA SSD

    为什么要采用PCI-E通道 目前在固态硬盘SSD中,有一部分采用了SATA3.0接口,而一些高端的固态硬盘则采用了PCI-E接口.那么为什么高端固态硬盘要采用PCI-E接口呢?为了弄清楚这个问题,先看 ...

  8. WEB集群 - LNMT集群架构部署zrlog

    目录 1. 集群环境说明 2. NFS部署 3. mysql部署 4. redis部署 5. tomcat部署 6. nginx负载均衡部署 7. 客户端访问 8. tomcat+redis实现会话共 ...

  9. 记一次ThreadLocal中的用户信息混乱问题

    前言 记录一次开发中遇到的关于 ThreadLocal 问题,场景是数据库表中的操作人总是无缘无故的被更改,排查了几遍代码才发现是 ThreadLocal 没有及时清理导致的. 一.为什么使用 Thr ...

  10. OpenStack 的 SR-IOV 虚拟机热迁移

    目录 文章目录 目录 前言列表 前言 SR-IOV Pass-through 虚拟机热迁移的问题 基于 macvtap 层的 SR-IOV 虚拟机热迁移 Workaround SR-IOV Pass- ...