tangyuxian
文章79
标签37
分类5
浏览器-页面渲染过程

浏览器-页面渲染过程

从解析html构建dom树开始分析

一 概览

1、解析HTML,生成DOM树;

2、解析CSS,生成CSS规则树;

3、合并DOM树和CSS规则树,生成render树;

4、布局render树(layout/reflow);

5、绘制render树(print),绘制页面像素信息;

6、浏览器将各层的信息发送给GUI,GUI将各层合成,显示在屏幕上。

二 分析

1 构建dom树

构建过程:Bytes->Characters->Tokens->Nodes->Dom

img

55fe0d9b8e517df129302bf703cd2628.png

网络中传输的内容其实是0和1这种字节数据,浏览器在收到字节数据后,才将字节数据转换为字符串;

当数据转换为字符串以后,浏览器会先将这些字符串通过词法分析转换为标记(token),这一过程叫做标记化。Token中会标识出当前Token是“开始标签”还是“结束标签”亦或是“文本”等信息。

结束化结束之后,这些标记紧接着就会被转换为Node,这些Node会根据不同Node之前的联系生成DOM树(Document Object Model)。

除了HTML文件还有CSS文件和JS文件。

2 构建CSSOM树

构建过程:Bytes->Characters->Tokens->Nodes->CSSOM

img

img

在这个过程中,浏览器会确定下一个节点的样式,并且这个过程是非常消耗资源的。因为节点的样式可以直接设置,也可以通过继承获得,浏览器必须递归CSSOM树才能确定具体的元素的样式。

3 构建渲染树(呈现树Render Tree)

合并DOM树和CSSOM树构建渲染树

1、过滤掉不可见节点(脚本标记、元标记)

2、过滤掉样式隐藏的节点(display:none)

根据渲染树来布局,计算节点的几何信息(layout)

将各个节点绘制在屏幕上(paint)

img

只有当DOM树和CSSOM树都构建完成之后才可以进行渲染树的构建,所以这两步是对整体渲染起阻塞作用的,当然了DOM树是必须的,它提供给页面内容,而CSSOM的必要性并不是太明显,所以在CSSOM构建的过程中可以做一些优化。在做优化前先要了解这几个知识点。

1、默认情况下,CSS是阻塞渲染的资源

2、我们可以通过媒体查询和媒体类型把一部分CSS标记为不阻塞渲染

(媒体查询的不足就是会严重影响关键渲染路径的性能)

3、浏览器会下载所有CSS资源、无论它阻塞还是不阻塞

根据上面三个知识点,会让你很清晰的知道,CSS优化可以做的事情就是,根据不同CSS使用场景和优先级的不同进行不阻塞标记。

如果是必要的CSS就请尽早的加载(1、引用位置靠前,2、减小文件体积)到客户端,这样就减少了对首次渲染的阻塞

三 JavaScript对渲染的影响

可以修改网页的方方面面,内容、样式、以及响应用户的交互。不过,javascript也会阻止DOM的构建和延缓网页渲染。下面我们来了解一下javascript和DOM、CSSOM的依赖关系。

javascript能修改内容和样式

无论(内联javascript还是外部javascript文件)都会阻止DOM的构建

DOM构建过程中如果遇到(非异步加载async)的javascript标签,浏览器将会终止DOM的构建,立即执行javascript。

这就是为什么非异步执行的javascript要放在尾部或者将可执行代码要放在DOMContentLoaded回调中?

因为如果该javascript代码操作了未构建完的DOM节点就会因为无法获取该节点而无法执行响应的操作。

CSSOM的构建影响javascript的执行

如果在浏览器尚未完成CSSOM的下载和构建时,去运行javascript脚本,那么浏览器会延迟脚本的执行和DOM的构建,直至完成CSSOM的下载和构建。可以这样理解,当出现非异步加载的javascript时,CSSOM构建完成时间是早于javascript的执行,两者早于DOMContentloaded(即DOM构建彻底完成)。

未优化–javascript正常加载

img

优化后–javascript异步加载

img

根据上面的分析,我们可以清楚的认识到,非必要优先加载的js,选择异步加载是最优选择。
为什么大家普遍把这样的代码放在body最底部?

JS文件不止会阻塞DOM的构建,也会导致CSSOM的构建。不完整的CSSOM是无法使用的,JavaScript想要访问CSSOM并更改它,就必须得到完整的CSSOM。所以导致浏览器在未完成CSSOM的构建的时候想要运行JavaScript。这种情况下,浏览器会先下载和构建CSSOM,然后再执行JavaScript。

JavaScript并不是必须放在底部,我们可以为script标签添加属性:

defer属性,表示js文件会并行下载,但是会放到HTML解析完成后顺序执行。

async属性,对于没有任何依赖的js文件可以使用,表示JS文件下载和解析不会阻塞渲染。

async与defer的区别在于,如果已经加载好,就会开始执行,即使仍在HTML解析阶段,所以这种方式加载的JavaScript依然会阻塞load事件。

async-scrapt可能在DOMContentLoaded触发直线或之后执行,但一定在load之前执行,所以多个async-script的执行顺序是不确定的。

四 优化策略

1、从文件大小考虑

2、将css放在头部,将js放在尾部

3、减少资源请求数量

4、下载的内容是否要在首屏上使用

5、script标签的使用加defer或async属性。

测试工具:Lighthouse 可以快速测试你的网页,并提供性能报告

监控工具:Nivigation Timing Api 设置你的代码,实时监控用户使用过程中的性能。


参考文档:

csdn文章:页面dom渲染过程

csdn文章:页面渲染原理

segmentfault文章:浏览器渲染页面过程与页面优化

扩展阅读:掘金文章:从输入url到页面展示发生了什么?

本文作者:tangyuxian
本文链接:https://www.tangyuxian.com/2021/03/22/%E5%89%8D%E7%AB%AF/%E6%B5%8F%E8%A7%88%E5%99%A8/%E6%B5%8F%E8%A7%88%E5%99%A8-%E9%A1%B5%E9%9D%A2%E6%B8%B2%E6%9F%93%E8%BF%87%E7%A8%8B/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可