浏览器的渲染原理
# 浏览器的渲染原理
# 简述一下浏览器的渲染过程重要
首先解析收到的文档,根据文档的定义构建一棵
DOM树,DOM树是由DOM元素及属性节点组成的然后对
css进行解析,生成CSSOM树根据
DOM树和CSSOM树构建render树,渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和DOM元素相对应,但是不可见的DOM元素不会被插入render树上,还有一个dom元素对应几个可见对象,是一些具有复杂结构的元素,无法用矩形来描述当渲染对象被创建并添加到树中,他们没有位置和大小,所以当浏览器生成
render树之后,会根据render树进行布局(也可以叫回流),此时浏览器要弄清楚各个节点在页面的确切位置和大小,也被称为自动重排布局结束后是绘制阶段,遍历渲染树并调用渲染对象的
paint方法将内容显示在屏幕上,绘制使用UI基础组件

注意
这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html 都解析完成之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容
# 介绍一下浏览器的渲染优化
针对
JS:js会阻塞html解析,也会阻塞css解析- 尽量将
js文件放在body最后 body中间尽量不要写script标签script的三种引入方式<script>立即停止页面渲染去加载资源文件,当资源加载完毕后立即执行js代码,js代码执行完毕后继续渲染页面;async是在下载完成之后,立即异步加载,加载好后立即执行,多个带async属性的标签,不能保证加载的顺序;defer是在下载完成之后,立即异步加载。加载好后,如果DOM树还没构建好,则先等DOM树解析好再执行;如果DOM树已经准备好,则立即执行。多个带defer属性的标签,按照顺序执行。
- 尽量将
针对
CSS:使用css的方法:link@import内联样式link:浏览器会派发一个线程(http线程)去加载资源文件,同时GUI渲染线程会继续向下渲染代码@import:GUI渲染线程会暂停渲染,去服务器加载资源文件,资源文件没返回之前不会继续渲染(阻碍浏览器渲染)内联样式:
GUI线程直接渲染注意⚠️
外部样式如果长时间没有加载完毕,浏览器为了用户体验,会使用浏览器会默认样式,确保首次渲染的速度。所以
CSS一般写在header中,让浏览器尽快发送请求去获取css样式。所以,在开发过程中,导入外部样式使用link,而不用@import。如果css少,尽可能采用内嵌样式,直接写在style标签中
针对
DOM树CSSOM树html文件代码层级尽量不要太深- 使用语义化标签,避免不标准语义化的特殊处理
- 减少
css代码的层级,因为选择器是从右向左进行解析的
减少回流和重绘
- 操作
dom时,尽量在底层级的dom节点进行操作 - 不要使用
table,一个小的改动会使整个table进行重新布局 - 不能频繁操作元素的样式,对于静态页面,尽量修改类名,而不是样式
- 使用
css表达式 - 使用
absolute或者fixed,使元素脱离文档流,这样发生变化不会影响其他元素 - 避免频繁操作
dom,可以创建一个文档片段,documentFragment,在他上面应用所有dom操作,然后把他添加到文档中 - 将元素先
display:none,操作结束后再把他显示出来,因为display为none中进行dom操作不会引发回流和重绘 - 将
dom的多个读写操作放在一个,而不是读写操作穿插,得益于浏览器的渲染队列机制
值得注意的是⚠️
浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流、重绘变成一次回流重绘。 将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发一次回流。
- 操作
# 渲染过程中遇到的js文件如何处理
js的加载、解析和执行会阻塞文档的解析,也就是说在构建dom是,html解析器遇到js的话会暂停文档的解析,然后将控制权移交给JS引擎,等他完成之后浏览器再从中断等地方恢复继续解析文档,也就是说,如果想要首屏渲染地越快,就越不应该再首屏加载JS文件,还是建议把script放到body的底部,或者加上defer或async
# 什么是文档的预解析
webkit和firefox都做了优化,当执行JS脚本的时候,另一个线程解析剩下的文档并加载后面需要用到的网络资源,这种方式可以使资源并行加载从而使整体速度更快,值得注意的是预解析并不改变dom树,他将整个工作留给了主解析过程,自己只解析外部资源的引用,比如外部script、css、image
# CSS如何阻塞文档解析
理论上即使css不改变dom树,也就没有必要停下文档的解析等待他们,然而当JS执行时可能在文档解析过程中请求样式信息,如果样式还没有加载和解析,脚本将得到错误的值,显然会导致很多问题,所以如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟 JavaScript 脚本执行和文档的解析,直至其完成 CSSOM 的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建 CSSOM,然后再执行 JavaScript,最后再继续文档的解析
# js脚本放在头部一定会造成阻塞吗
不一定会
如果js并没有引用dom的话就不会造成阻塞,当然也不排除其他情况阻塞,但是并不会一定造成阻塞
# 如何优化关键渲染路径
为了尽快完成首次渲染,需要最大限度减小以下三种可变因素
关键资源的数量
关键路径长度
关键字节的数量
关键资源是可能阻止网页首次渲染的资源。
这些资源越少,浏览器的工作量就越小,对 CPU 以及其他资源的占用也就越少。同样,关键路径长度受所有关键资源与其字节大小之间依赖关系图的影响:某些资源只能在上一资源处理完毕之后才能开始下载,并且资源越大,下载所需的往返次数就越多。
最后,浏览器需要下载的关键字节越少,处理内容并让其出现在屏幕上的速度就越快。要减少字节数,我们可以减少资源数(将它们删除或设为非关键资源),此外还要压缩和优化各项资源,确保最大限度减小传送大小。
优化关键渲染路径的常规步骤如下:
- 对关键路径进行分析和特性描述:资源数、字节数、长度。
- 最大限度减少关键资源的数量:删除它们,延迟它们的下载,将它们标记为异步等。
- 优化关键字节数以缩短下载时间(往返次数)。
- 优化其余关键资源的加载顺序:您需要尽早下载所有关键资源,以缩短关键路径长度
# 什么情况下会阻塞渲染
首先渲染的前提是生成render树,所以html和css肯定会阻止渲染,如果想更快,那只能降低文件大小或者压缩,扁平层级,优化选择器。然后当浏览器解析到script标签时,会暂停构建DOM,完成之后才会从暂停的地方重新开始,所以想更快,就不应该在首屏加载JS文件,全是建议在body标签底部写入script标签的
或者给script标签加上defer或者async属性,都会并行下载,同时不会阻塞渲染