CSS继承规则
在DOM树中子节点会继承父节点的样式,例如body设置了font-size:20px,那么嵌套在其中的p标签就会继承body的CSS属性,隐式含有CSS中font-size:20px。
CSS层叠规则
记得填坑
渲染流水线
子阶段
● 构建DOM树
● 样式计算
● 布局阶段
● 分层
● 绘制
● 分块
● 光栅化
● 合成
每个子阶段都有输入内容、处理过程、输出内容
构建DOM树
浏览器无法直接理解和使用HTML,所以需要将HTML转换为浏览器能够理解的结构——DOM树
通过HTML标签的层次来判断父子节点。
DOM和HTML内容几乎一致,但是DOM是保存在内存中的树状结构
通过JavaScript的document类来修改DOM内容。
document.getElementByTagName(‘p’).innerText = ‘hello’;
样式计算
CSS的样式来源
● link引用外部CSS文件
● style标记内的CSS
● style属性内嵌的CSS
CSS转换为浏览器理解的styleSheets(CSSOM树)
当渲染引擎接收到CSS文件时,会执行转换操作,将CSS转换为styleSheets
在控制台输入document.styleSheets就可以查看结构
转换样式表中的属性值,使其标准化
将单位转换为默认单位,例如em转换为px,blue颜色转换为rgb(0,0,255),font-weight:bold转换为700数值
计算DOM中每个节点的具体样式
通过CSS的继承规则和层叠规则来决定各个元素的样式
布局阶段
创建布局树
在渲染页面之前,构建一棵仅包含可见元素的布局树
display:none;//css中拥有该属性的不会被布局树所包纳
布局计算
计算各个组件的布局
记得填坑
CSS文件下载阻塞不会阻塞DOM树的解析,但是会阻塞DOM树的渲染,从而影响了页面的显示。
解决办法:
● 使用CDN,中间代理来加快文件传输速度
● CSS压缩,webpack和gulp等打包工具
● 合理使用缓存,设置缓存控制表达式和etag
因为DOM树的解析和CSS的解析是并发的,所以不会有影响,但是render tree需要DOM树和cssom树,所以会阻塞页面显示
CSS阻塞渲染的情况
● 当CSS文件在JS中运行时,正常解析,不会阻塞。
● 当JS文件中引用了外部CSS,当解析到JS时,会停止渲染,开始下载外部文件,下载完成后执行JS文件,再继续往下解析DOM。
分层
浏览器的页面实际上被分为了很多图层,因为有index-z属性的存在。渲染引擎还需要为这些特定的节点生成专用的图层,并生成一棵对应的图层树。
倘若一个子节点没有对应的层,那么其从属于父节点的图层
拥有层叠上下层的图层会被划分为单独的一层
图层绘制
每一个图层的绘制会拆分成很多小的绘制指令,然后根据顺序组装成一个待绘制列表
开发者工具的Layers标签的document层可以查看绘制列表和绘制过程
栅格化
定义
将图块转换为位图
绘制操作是由渲染引擎的合成线程完成,当绘制列表准备好后,主线程把绘制列表提.交给合成线程。
视口
用户在屏幕上可见的页面区域被称作视口
因为如果一口气绘制所有页面,会导致时间开销很大,所以合成线程将图层划分为图块,根据视口附近的图块优先生成位图。
栅格化过程会使用GPU加速生成,该过程被称作快速栅格化
快速栅格化由渲染进程把生成图块的指令发给GPU,然后在GPU中执行生成图块的位图,并保存在GPU内存中。
合成和显示
当所有图块被栅格化,合成线程就会生成绘制图块的命令“DrawQuad”,并提交给浏览器进程
浏览器进程有一个viz组件,接收DrawQuad命令并将页面内容绘制到内存,再显示到屏幕
总结
浏览器渲染页面的步骤如下
- 首先根据HTML文件构建DOM树
- 渲染引擎将CSS转换为styleSheets,计算DOM节点的样式
- 创建布局树,计算元素布局信息
- 对布局树分层,生成分层树
- 为图层生成绘制列表,并提交给合成线程
- 合成线程将图层分为图块,并在光栅化线程池将图块转换为位图
- 合成线程发送DrawQuad命令给浏览器进程
- 浏览器进程根据信息生成页面并显示到屏幕上
优化
重排
倘若通过CSS或者JavaScript修改了元素的几何位置属性,如高宽等,那么就会从布局重新进行渲染流水线,这修改之后的一系列子阶段,叫做重排。
重排需要更新完整的渲染流水线,开销最大
重绘
通过JavaScript改变某些元素的背景颜色,布局阶段将不受影响,所以是从绘制阶段开始之后的一系列子阶段,被称为重绘。
重绘省去了布局和分层的阶段,所以执行效率比重排更高
合成
合成就是把分层的图层合并在一起。
使用css的transform实现动画效果,可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。效率最高,没有占用主线程资源,避开了布局和绘制两个子阶段。相对上面两个绘制效率大大提升。
部分css动画是在合成线程上运行,没有涉及到主线程,不会受到重绘和重排的影响。
而JS动画是在渲染进程的主线程上运行,效率会大大降低。
分块
合成线程将每个图层分割为大小固定的图块,优先绘制靠近视口的图块,可以大大加速页面的显示速度
优化纹理上传问题
因为计算机内存传到GPU内存操作较慢,所以谷歌采用了一个策略:首次合成图块会使用一个低分辨率的图片,正常比例的页面内容绘制完成后,再替换掉当前显示的低分辨率内容。
will-change
用will-change属性来为元素准备一个独立层,这样可以减少对页面卡顿的影响,不过同时会大大增加占用内存。
重绘与重排的优化
● 将需要多次重排的组件position设置为fixed或者absolute,脱离文件流。
● 直接改变元素的classname
● 更改前先设置元素为display:none,然后进行页面布局等操作,再将该元素display:block,这样可以只触发两次重排
● 如果创建多个节点,可以等documentfragment创建完后一次性加入document