CRP关键渲染路径笔记
关键渲染路径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()- 要获取的元素在
shadow DOM中 - 使用了
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
- 获取以下某一种属性
- 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逻辑所以下方的两个时间戳可以帮我们记录花费的时间EventStartEventEnd
domComplete:所有处理完成并且网页上所有资源比如图像也已下载完毕,浏览器加载状态结束loadEvent:浏览器触发onload的事件触发额外的应用逻辑StartEnd
关键渲染路径优化
像素管道
像素管道是一个对页面渲染每个像素的处理流程的一种抽象化描述,像素管道包含以下几个关键节点
- JavaScript:JavaScript实现的视觉效果,修改DOM、CSS Animations、Transitions、Web Animation API
- Style:计算CSS应用范围,具体标签选择器对应DOM节点
- Layout:计算布局,当页面元素位置或页面宽度元素宽度发生变化时会发生回流Reflow其实就是布局的重新计算
- Paint:填充像素,它涉及绘出文本、颜色、图像、边框和阴影,基本包括元素的每个可视部分,绘制是分布在不同层Layor上的
- Composite:合成,由于页面可能会分布在多层,合成的目的就是将所有的层按照顺序渲染出来
不是所有帧都会经过整个像素管道的处理,一般有三种情况:
JS/CSS → Style → Layout → Paint → Composite

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

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

如果更改了既不会导致回流,也不会造成重绘的操作,就会只进行样式计算和布局和合成操作,这种操作最轻量化,可以用在需要页面高性能的环境下例如动画或滚动场景。一个最重要的例子就是
transform、perspective、CSS样式的改变在Blink(Chrome的渲染引擎)和Gecko(Firefox的渲染引擎)中只会触发合成操作,开支非常小。
CSS Triggers网站提供了在这些CSS变化时会进行的以上三个流程的哪一个
优化建议
- 用
transform、perspective做形变和位移减少reflow - 避免逐个修改节点样式尽量一次性修改
- 使用
DocumentFragment需要多次追加元素或修改的内容缓存起来,最后一次性挂载到DOM上 - 需要多次修改的元素可以使用
display:none先从DOM节点上移出,操作完再标记display:block放回DOM上,因为不在DOM上的元素修改不会导致重绘 - 避免多次读取某些属性
- 使用
Flexbox可以显著缩短布局耗费的时间 - 通过绝对定位将复杂的节点元素脱离文档流,形成新的Layer,降低回流成本
渲染优化建议
- 样式文件应该在
head尽快下载执行(CSS下载解析在独立线程中不影响DOM下载解析)、JavaScript放在body结束前(下载解析会阻塞DOM解析且JS有可能修改DOM元素,所以等到DOM内容基本完成解析再下载执行) - 优化CSS选择器,尽量减少层级嵌套,减少子选择等
- DOM的读写操作应该放在一起,读读读写写写但是不要读写读写读写
- 不要一条一条改变样式,尽量放到一个类中,或将元素脱离文档再修改样式
position为absolute或fixed的元素重排开销较小,
总结
渲染流程
- 浏览器引擎边下载HTML边构建DOM树,以User Agent Stylesheet(浏览器内置样式)为原料构建CSSOM树
- 如果HTML解析到
<script>- 默认下载与执行都阻塞DOM树的构建,
inline的就阻塞并执行 defer开另外线程下载但推迟执行到DOM树构建完毕且在DOMContentLoaded之前async开启另外线程下载但下载完立即阻塞DOM树构建并执行
- 默认下载与执行都阻塞DOM树的构建,
- 如果HTML解析到CSS,不论
inline内联、internal元素上还是外联的CSS(外联的需要下载完成之后)刷新CSSOM树,这个过程与DOM树生成是在不同线程不会阻塞DOM的生成- CSSOM中不会出现不可见的元素,包括
display:none的元素以及其子元素,但visibility:hidden或opacity:0的元素还是会被添加到CSSOM上
- CSSOM中不会出现不可见的元素,包括
- 等到DOM树完成生成(JavaScript也执行完毕)、CSSOM树构建完成之后执行布局
- 布局完成之后执行绘制操作
性能优化
根据上方优化性能的操作编写代码与执行DOM操作
可能阻塞CRP的情况
HTML
如果HTML本身加载就很慢,就不用谈其他的了
CSS
CSS下载解析阻塞渲染树的创建与JavaScript的执行,但动态插入的外链CSS不会阻塞页面渲染,动态插入的内联CSS不会阻塞DOM的解析和渲染
JavaScript
同步的JavaScript会阻塞DOM的解析和渲染,异步的JavaScriptdefer和async在下载时都不会影响DOM的生成,但async会在下载完成之后执行时阻塞DOM生成,动态插入的外链JavaScript脚本不会阻塞DOM解析生成
CRP关键渲染路径笔记的更多相关文章
- 优化关键渲染路径CRP
什么是关键渲染路径? 从收到 HTML.CSS 和 JavaScript 字节到对其进行必需的处理,从而将它们转变成渲染的像素这一过程中有一些中间步骤 浏览器渲染页面前需要先构建 DOM 和 CSSO ...
- 基于Webkit的浏览器关键渲染路径介绍
关键渲染路径概念 浏览器是如何将HTML.JS.CSS.image等资源渲染成可视化的页面的呢?本文简单介绍一下渲染过程中涉及到的关键步骤. 该过程分为四步:模型对象的构建.渲染树构建.布局.绘制. ...
- [Unity Shader笔记]渲染路径--Forward渲染路径
[Unity Shader笔记]渲染路径--Forward渲染路径 (2014-04-22 20:08:25) 转载▼ 标签: shader unity renderingpath forward 游 ...
- Unity Lighting - Choosing a Rendering Path 选择渲染路径(三)
Choosing a Rendering Path 选择渲染路径 Unity supports a number of rendering techniques, or ‘paths’. An i ...
- 离屏渲染学习笔记 /iOS圆角性能问题
离屏渲染学习笔记 一.概念理解 OpenGL中,GPU屏幕渲染有以下两种方式: On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行. O ...
- Unity3D光照前置知识——Rendering Paths(渲染路径)及LightMode(光照模式)译解
简述 Unity supports different Rendering Paths. You should choose which one you use depending on your g ...
- 前向渲染路径细节 Forward Rendering Path Details
正向渲染路径细节 Forward Rendering Path Details Forward Rendering path renders each object in one or more pa ...
- RenderingPath 渲染路径
http://blog.csdn.net/lichaoguan/article/details/42554821 RenderingPath 渲染路径 Deferred Lighting 延时光照 延 ...
- 渲染路径-Deferred Lighting 延时光照
http://blog.csdn.net/heyuchang666/article/details/51564954 注意: 最后3个步骤注意下 延时光照是有着最高保真度的光照和阴影的渲染路径.如果你 ...
- 渲染路径-u3d渲染路径比较
Unity支持不同的渲染路径.应具体取决于你的游戏内容和目标平台/硬件来选择使用哪一个.不同的渲染路径有不同的特点和性能特点,主要影响灯光和阴影. 项目所使用的渲染路径在Player S ...
随机推荐
- 基于 Flink SQL 构建流批一体的 ETL 数据集成
简介: 如何利用 Flink SQL 构建流批一体的 ETL 数据集成. 本文整理自云邪.雪尽在 Flink Forward Asia 2020 的分享,该分享以 4 个章节来详细介绍如何利用 Fli ...
- 使用MQTT与函数计算做热力图的实践
简介: 在各类场景中,关于上报数据的处理无处不在,而以上提到的场景都可以通过本方案的MQTT+FC+API Gateway的方式参考优化来实现. 前言 最近几年,我们在一些商场.图书馆.机场或港口环境 ...
- [FAQ] FinalCutPro 竖版视频 加模糊背景变 横版视频
把一段影片拖到时间轴上面,注意自定义尺寸选择 1920 x 1080,因为竖版的是 1080 x 1920. 切换到仅视频,并选择变形,视频区左右拖动视频到最大. 设置模糊效果为高斯曲线. 切回到全部 ...
- WPF 基础 2D 图形学知识 判断点是否在线段上
在知道一个使用两个点表示的线段,和另一个点,求另一个点是否在线段上 本文算法属于通用的算法,可以在 WPF 和 UWP 和 Xamarin 等上运行,基本上所有的 .NET 平台都能执行 如下图,如果 ...
- GitHub 的 Action 判断仅在主仓库才执行脚本
我有一个 GitHub 项目,这个项目配置了仅需要在源仓库才能执行的 Action 如推送 NuGet 等发布动作.如何在 Action 里面设置让 Fork 的仓库不执行 Action 的步骤 想要 ...
- 2018-11-14-git无法pull仓库refusing-to-merge-unrelated-histories
title author date CreateTime categories git无法pull仓库refusing to merge unrelated histories lindexi 201 ...
- RT-Thread线程同步与线程通信
一.线程同步 线程同步的使用场景 例如一项工作中的两个线程:一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递: ...
- SpringBoot中几种好用的代码生成器(基于Mybatis-plus生成entity、mapper、xml等)
前言 熟悉Spring框架的同学一定都知道MVC开发模式吧,控制器(Controller).业务类(Service).持久层(Repository).数据库映射(Mapper).各种DO类构成了我们服 ...
- 用友BIP全面预算
全面预算是企业在经营过程中制定并实施的一种财务管理工具,它考虑了企业的各个方面,包括销售.采购.生产.财务.人力资源等,以全面的视角规划和控制企业的财务活动. 用友BIP全面预算数智化解决方案利用了& ...
- 通俗易懂的KMP理论讲解(含手求Next数组)
通俗易懂的KMP理论讲解(含手求Next数组) 1.KMP算法介绍 KMP算法的核心是利用匹配失败后的信息,通过一个 next 数组,保存模式串中前后最长公共子序列的长度,尽量减少模式串与主串的匹配次 ...