浏览器渲染原理

当咱们打开一个浏览器,输入一个url,按下回车,不用多久,一个漂亮的页面就呈如今咱们眼前了。其实,在咱们等待页面打开的这段时间里,浏览器但是作了很多的事情,究竟是什么事情呢,就咱们一块儿来看看吧css

首先,咱们须要先了解一下什么是页面渲染以及页面渲染的整个过程~html

 

一、页面渲染浏览器

简单地说,页面渲染就是浏览器将 HTML 代码根据 CSS 定义的规则显示在浏览器窗口中的这个过程服务器

二、渲染过程布局

1. 用户输入网址(假设是个 HTML 页面,而且是第一次访问),浏览器向服务器发出请求,服务器返回 HTML 文件;性能

2. 浏览器开始载入 HTML 代码,发现 <head> 标签内有一个 <link> 标签引用外部 CSS 文件;学习

3. 浏览器又发出 CSS 文件的请求,服务器返回这个 CSS 文件;字体

4. 浏览器继续载入 HTML 中 <body> 部分的代码,而且 CSS 文件已经拿到手了,能够开始渲染页面了;优化

5. 浏览器在代码中发现一个 <img> 标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;动画

6. 服务器返回图片文件,因为图片占用了必定面积,影响了后面段落的排布,所以浏览器须要回过头来从新渲染这部分代码;

7. 浏览器发现了一个包含一行 JavaScript 代码的 <script> 标签,赶快运行它;

8. JavaScript 脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个 <div>(style.display=”none”)。杯具啊,忽然就少了这么一个元素,浏览器不得不从新渲染这部分代码;

9. 终于等到了 </html> 的到来,浏览器泪流满面……

10. 等等,还没完,用户点了一下界面中的“换肤”按钮,JavaScript 让浏览器换了一下 <link> 标签的 CSS 路径;

11. 浏览器召集了在座的各位 <div><span><ul><li> 们,“大伙儿收拾收拾行李,咱得从新来过……”,浏览器向服务器请求了新的CSS文件,从新渲染页面。

 

如今你们已经知道什么是页面渲染了吧,接下来再介绍两个概念,reflow和repaint~

三、reflow(回流)

 

首先说一下页面为何会慢?那是由于浏览器要花时间、花精力去渲染,尤为是当它发现某个部分发生了点变化影响了布局,须要倒回去从新渲染,内行称这个回退的过程叫 reflow

下面这张图很清晰的说明了这个过程

reflow 几乎是没法避免的。如今界面上流行的一些效果,好比树状目录的折叠、展开(实质上是元素的显 示与隐藏)等,都将引发浏览器的 reflow。鼠标滑过、点击……只要这些行为引发了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引发它内部、周围甚至整个页面的从新渲 染。一般咱们都没法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。

 

固然,reflow 问题是能够优化的,咱们能够尽可能减小没必要要的 reflow。好比开头的例子中的 <img> 图片载入问题,这其实就是一个能够避免的 reflow —— 给图片设置宽度和高度就能够了。这样浏览器就知道了图片的占位面积,在载入图片前就预留好了位置。

四、repaint(重绘)

另外,有个和 reflow 看上去差很少的术语:repaint,中文叫重绘。若是只是改变某个元素的背景色、文 字颜色、边框颜色等等不影响它周围或内部布局的属性,将只会引发浏览器 repaint。repaint 的速度明显快于 reflow(在IE下须要换一下说法,reflow 要比 repaint 更缓慢)。

 

学习了上面的知识后,相信你们对浏览器渲染已经有了一个比较深入的了解了吧,接下来让咱们进行更深刻的学习

五、浏览器工做大体流程

从上面这个图中,咱们能够看到那么几个事:

  1)浏览器会解析三个东西:

  • 一个是 HTML/SVG/XHTML,事实上,Webkit 有三个 C++ 的类对应这三类文档。解析这三种文件会产生一个 DOM Tree。
  • CSS,解析 CSS 会产生 CSS 规则树。
  • Javascript,脚本,主要是经过 DOM API 和 CSSOM API 来操做 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解析

HTML 的 DOM Tree 解析以下:

上面这段 HTML 会解析成这样:

下面是另外一个有 SVG 标签的状况:

七、CSS 解析

CSS 的解析大概是下面这个样子(下面主要说的是 Gecko 也就是 Firefox 的玩法),假设咱们有下面的 HTML 文档:

因而 DOM Tree 是这个样子:

而后咱们的 CSS 文档是这样的:

因而咱们的 CSS Rule Tree 会是这个样子:

注意,图中的第 4 条规则出现了两次,一次是独立的,一次是在规则 3 的子结点。因此,咱们能够知道,创建 CSS Rule Tree 是须要比照着 DOM Tree 来的。CSS 匹配 DOM Tree 主要是从右到左解析 CSS 的 Selector,好多人觉得这个事会比较快,其实并不必定。关键还看咱们的 CSS 的 Selector 怎么写了。

注意:CSS 匹配 HTML 元素是一个至关复杂和有性能问题的事情。因此,你就会在N多地方看到不少人都告诉你,DOM 树要小,CSS 尽可能用 id 和 class,千万不要过渡层叠下去,……

经过这两个树,咱们能够获得一个叫 Style Context Tree,也就是下面这样(把 CSS Rule 结点 Attach 到 DOM Tree 上):

因此,Firefox 基本上来讲是经过 CSS 解析生成 CSS Rule Tree,而后,经过比对 DOM 生成 Style Context Tree,而后 Firefox 经过把 Style Context Tree 和其 Render Tree(Frame Tree)关联上,就完成了。注意:Render Tree 会把一些不可见的结点去除掉。而 Firefox 中所谓的 Frame 就是一个 DOM 结点,不要被其名字所迷惑了

 

八、重说渲染

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

(1)计算 CSS 样式

(2)构建 Render Tree

(3)Layout – 定位坐标和大小,是否换行,各类 position, overflow, z-index 属性 ……

(4)正式开画

注意:上图流程中有不少链接线,这表示了 Javascript 动态修改了 DOM 属性或是 CSS 属性会致使从新 Layout,有些改变不会,就是那些指到天上的箭头,好比,修改后的 CSS rule 没有被匹配到,等。

  这里从新说下两个概念,一个是 Reflow,另外一个是 Repaint。这两个不是一回事。

  • Repaint——屏幕的一部分要重画,好比某个 CSS 的背景色变了。可是元素的几何尺寸没有变。
  • Reflow——意味着元件的几何尺寸变了,咱们须要从新验证并计算 Render Tree。是 Render Tree 的一部分或所有发生了变化。这就是 Reflow,或是 Layout。(HTML 使用的是 flow based layout,也就是流式布局,因此,若是某元件的几何尺寸发生了变化,须要从新布局,也就叫 reflow)reflow 会从 <html> 这个 root frame 开始递归往下,依次计算全部的结点几何尺寸和位置,在 reflow 过程当中,可能会增长一些 frame,好比一个文本字符串必需被包装起来。
  • 因此,下面这些动做有很大可能会是成本比较高的。

    • 当你增长、删除、修改 DOM 结点时,会致使 Reflow 或 Repaint。
    • 当你移动 DOM 的位置,或是搞个动画的时候。
    • 当你修改 CSS 样式的时候。
    • 当你 Resize 窗口的时候(移动端没有这个问题),或是滚动的时候。
    • 当你修改网页的默认字体时。

      注:display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,由于没有发现位置变化。

 

九、一些优化建议

(1)不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,而后修改 DOM 的 className

 

(2)把 DOM 离线后修改。如:

  • 使用 documentFragment 对象在内存里操做 DOM。
  • 先把 DOM 给 display:none (有一次 repaint),而后你想怎么改就怎么改。好比修改 100 次,而后再把他显示出来。
  • clone 一个 DOM 结点到内存里,而后想怎么改就怎么改,改完后,和在线的那个的交换一下。

(3)不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。否则这会致使大量地读写这个结点的属性。

(4)尽量的修改层级比较低的 DOM。固然,改变层级比较底的 DOM 有可能会形成大面积的 reflow,可是也可能影响范围很小。

(5)为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。

(6)千万不要使用 table 布局。由于可能很小的一个小改动会形成整个 table 的从新布局。