VueBloghyhero6

浏览器渲染原理简介笔记

2019-12-23 / 2019-12-23 / 337次浏览

之前被人问到过,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 浏览器和火狐又不一样。 文章上说 它是一个🌲

渲染
渲染的流程基本如下(黄色的四个步骤):

  1. 计算CSS 样式
  2. 构建Render Tree
  3. Layout -定位坐标 和大小,是否换行,各种position,overflow,z-index属性
  4. 正式开画

注意: 上图流程中有很多链接线,这表示了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

我只是弱弱的做个笔记,其实看完这篇笔记,起码对浏览器渲染有个基本认知。