之前被人问到过,dom 渲染原理是啥,啥米,渲染原理,我说一遍dom的加载顺序,比如css 先加载啊,之后标签渲染啊,最后加载js啊。人家又问,css 渲染原理是啥,虾米? 不是从右到左去查找最近的元素,匹配渲染嘛。啥,说的不是这个? 黑人问号脸。于是回来赶紧补救一波。
下面多图预警
网上有篇原文 《How Browsers Work》
http://taligarsiel.com/Projects/howbrowserswork1.htm
真的这个我不保证你能完全看完,其中随便一个小结弄出来就是一篇博客等。
然后又找到一篇博客,于是有了这篇笔记。
浏览器工作大流程
1.浏览器会解析三个东西:
一个是html/svg/XHTML,事实上,Webkit 有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。
css,解析css 会产生css 规则树。
javascript,脚本,主要是通过DOM API 和 CSSOM 来操作DOM Tree 和 CSS Rule Tree。
2.解析完成之后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:
Rendering Tree 渲染树🌲并不等同于DOM 树,因为一些像Header 或 display:none 的东西就没有必要放在渲染树中了。
CSS 的 Rule Tree 主要是为了完成匹配并把CSS Rule 附加上 Rendering Tree 上的每个Element。也就是DOM 结点。也就是所谓的Frame。
然后,计算每个Frame(每个Element)的位置,这又叫layout 和 reflow 过程。
3.最后通过调用操作系统Native GUI的API绘制。

DOM 解析







css 解析根据浏览器的不同(下面说的是Gecko也就是Firefox的玩法),假设我们有下面的HTML 文档:
<doc>
<title>A few quotes</title>
<para>
Franklin said that <quote>"A penny saved is a penny earned."</quote>
</para>
<para>
FDR said <quote>"We have nothing to fear but <span>fear itself.</span>"</quote>
</para>
</doc>

这就这个图 DOM 树

css 树🌲
注意,图中第4条规则出现了俩次,一次是独立的,一次是在规则3的子节点。所以,我们可以知道,建立CSS Rule Tree 是需要比照 DOM Tree 来的。 CSS 匹配 DOM Tree 主要是从右到左解析css 的Selector,好多以为这个事会比较快,其实并不一定,关键看我们的CSS 的Selector 怎么写了。( 看了一眼,改篇文章产自 2013年,当时的浏览器来看确实会有性能问题。但是现在css写法性能问题一般的项目几乎可以忽略不计,除非项目有特殊需要)
注意:CSS 匹配HTML元素是一个相当复杂和性能问题的事情。所以,你会在N多地方看到很多人都告诉你,DOM 树要小,CSS 尽量用ID 和 class,尽量减少过渡叠层的问题。(sass 尽量减少到不要超过3层)。
所以,Firefox 基本上来说是通过CSS 解析 成 CSS Rule Tree,然后,通过比对DOM 生成 Style Context Tree, 然后 Firefox 通过把 Style Context Tree 和 其 Render Tree (Frame Tree)关联上,就完成了。注意:Render Tree 会把一些不可见的节点 去除掉。而火狐所谓的
Frame 就是一个DOM 结点,不要被其名字所迷惑了。

然而 webkit 浏览器和火狐又不一样。 文章上说 它是一个🌲
渲染
渲染的流程基本如下(黄色的四个步骤):

注意: 上图流程中有很多链接线,这表示了javascript动态修改了DOM属性或者是CSS 属性导致 重新 Layout,有些改变不会,就是指向到天上的箭头,比如,修改后的css没有匹配到等等。
这里找俩个概念,一个是Reflow,另一个是Repaint。这俩个不是一回事
Repaint-- 屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。
Reflow-- 意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree.是Render Tree的一部分或全部发生变化。这就是Reflow, 或是Layout。 (HTML 使用的是flow baed layout,也就是流式布局,所以,如果某个元件的几何尺寸发生了变化,需要重新布局,也就叫reflow)reflow 会从这个root frame开始递归往下,依次计算所有的节点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。
Reflow的成本比Repaint的成本高得多的多。DOM Tree 里的每个节点都会有reflow方法,一个节点的reflow很有可能导致子节点,甚至父点以及同级节点的reflow。在高性能电脑上面没有什么问题。但是reflow 发生在手机上的话。这个过程就是比较痛苦和费电的。
所以,下面这些动作有很大可能是成本比较高的。
当你增加、删除、修改DOM节点时,会导致Reflow 或 Repaint
当你移动DOM 位置,或者搞个动画的时候。
当你修改CSS样式的时候。
当你Resize窗口的时候(移动端没有这个问题),或者是滚动的时候。
当你修改网页的默认字体时。
注:display:none 会触发reflow,而visibility:hidden 只会触发repaint,因为没有发现位置变化。
如果在屏幕滚动的时候,我们页面上的所有的像素都会跟着滚动,那么性能上没有什么问题,因为显卡对于对于这种把全屏像素往上往下移动的算法很快。但是如果你有一个fixed的背景图,或者有些Element不跟着滚动,有些Elment是动画,那么这个滚动动作对于浏览器来说会是相当痛苦的一个过程。滚屏的时候reflow 对性能的影响很大。
所以,下面这些动作有很大可能会是成本比较高的。
当你增加、删除、修改DOM节点时,会导致Reflow 或 Repaint
当你移动DOM的位置,或者搞个动画的时候。
当你修改CSS样式的时候。
当你Resize窗口的时候(移动端没有这个问题),或者是滚动的时候。
当你修改网页的默认字体的时。
基本上,reflow 有如下几个原因
Initial。 网页初始化的时候
Incremental。 一些Javascirpt 在操作DOM Tree时。
Resize。某些元件的尺寸变了。
StyleChange。 如果CSS的属性发生变化了。
Dirty。几个Incremental 的 reflow 发生在同一个frame的子树上。

这个是JS 在直接操作DOM
一般来说,浏览器会把这样的操作积攒一批,然后做一次reflow,这又叫做异步felow 或是增量异步feflow。
但是有些时候我们的一些JS代码,阻止了浏览器的增量异步。
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
IE中的 getComputedStyle(), 或 currentStyle
如果我们的程序需要这些值,那么浏览器需要返回最新的值,而这样一样会flush出一些样式的改变,从而造成频繁的reflow/repaint.
减少reflow/repaint

单独的修改不如批量的修改。
把DOM离线后修改:如:( 这个不就是现在的前端俩大框架Vue 和 React 这俩大框架实现的虚拟dom嘛,又回来了,只不过这篇文章太就远了。 )
使用documentFragment [ˈfræɡmənt , fræɡˈment]
对象在内存操作DOM
先把DOM给display:none( 有一次reflow ),然后你想怎么改就怎么改。比如修改100次再把它显示出来。
clone 一个DOM节点到内存里,然后想怎么改就怎么改,改完后和在线的那个交换下。
不要把DOM节点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个节点的属性
尽可能的修改层级比较低的DOM。当然,改变层级比较底的DOM可能会造成大面积reflow,但是也可能影响范围减小。
减少使用table 布局,一个小的改动会造成整个table的重新布局。
最后附上这位大佬的博客
https://coolshell.cn/articles/9666.html
我只是弱弱的做个笔记,其实看完这篇笔记,起码对浏览器渲染有个基本认知。
文章采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接。