浏览器渲染原理与过程

1、浏览器如何渲染网页

要了解浏览器渲染页面的过程,首先得知道一个名词——关键路径渲染。关键渲染路径(Critical Rendering Path)是指与当前用户操做有关的内容。例如用户在浏览器中打开一个页面,其中页面所显示的东西就是当前用户操做相关的内容,也就是浏览器从服务器那收到的HTML,CSS,JavaScript等相关资源,而后通过一系列处理后渲染出来的web页面。html

而浏览器渲染的过程主要包括如下五步:web

  • 浏览器将获取的HTML文档并解析成DOM树。后端

  • 处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)。浏览器

  • 将DOM和CSSOM合并为渲染树(rendering tree)将会被建立,表明一系列将被渲染的对象。服务器

  • 渲染树的每一个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只须要一次pass绘制操做就能够布局全部的元素。异步

  • 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting.布局

具体以下图过程以下图所示:性能

Webkit字体

须要注意的是,以上五个步骤并不必定一次性顺序完成,好比DOM或CSSOM被修改时,亦或是哪一个过程会重复执行,这样才能计算出哪些像素须要在屏幕上进行从新渲染。而在实际状况中,JavaScript和CSS的某些操做每每会屡次修改DOM或者CSSOM。优化

下面咱们就来详细的了解一下这几个过程及须要注意的事项。

2、浏览器渲染网页的具体流程

2.1 构建DOM树

当浏览器客户端从服务器那接受到HTML文档后,就会遍历文档节点而后生成DOM树,DOM树结构和HTML标签一一对应。须要注意记下几点:

  1. DOM树在构建的过程当中可能会被CSS和JS的加载而执行阻塞。(这在后面会详细介绍。)

  2. display:none 的元素也会在DOM树中。

  3. 注释也会在DOM树中

  4. script标签会在DOM树中

2.2 CSS解析

浏览器会解析CSS文件并生成CSS规则树,在过程当中,每一个CSS文件都会被分析成StyleSheet对象,每一个对象都包括CSS规则,CSS规则对象包括对应的选择器和声明对象以及其余对象。
在这个过程须要注意的是:

  • CSS解析能够与DOM解析同进行。

  • CSS解析与script的执行互斥 。

  • 在Webkit内核中进行了script执行优化,只有在JS访问CSS时才会发生互斥。

2.3 构建渲染树(Rendr tree construction)

经过DOM树和CSS规则树,浏览器就能够经过它两构建渲染树了。浏览器会先从DOM树的根节点开始遍历每一个可见节点,让后对每一个可见节点找到适配的CSS样式规则并应用。具体的规则有如下几点须要注意:

  • Render Tree和DOM Tree不彻底对应。

  • display: none的元素不在Render Tree中

  • visibility: hidden的元素在Render Tree中

2.4 渲染树布局(layout of the render tree)

布局阶段会从渲染树的更节点开始遍历,因为渲染树的每一个节点都是一个Render Object对象,包含宽高,位置,背景色等样式信息。因此浏览器就能够经过这些样式信息来肯定每一个节点对象在页面上的确切大小和位置,布局阶段的输出就是咱们常说的盒子模型,它会精确地捕获每一个元素在屏幕内的确切位置与大小。须要注意的是:

  • float元素,absoulte元素,fixed元素会发生位置偏移。

  • 咱们常说的脱离文档流,其实就是脱离Render Tree。

2.5 渲染树绘制(Painting the render tree)

在绘制阶段,浏览器会遍历渲染树,调用渲染器的paint()方法在屏幕上显示其内容。渲染树的绘制工做是由浏览器的UI后端组件完成的。

3、浏览器渲染网页的那些事儿

3.1 阻塞渲染

前面咱们有提到这方面的问题,说到资源的阻塞咱们清楚的是,现代浏览器老是并行加载自语言。例如当HTML解析器被脚本阻塞时,解析器虽然会中止构建DOM,但仍然会辨识该脚本后面的资源,并进行预加载。且因为如下两点。浏览器会延迟 JavaScript 的执行和 DOM 构建:

  • CSS 被默认被视为阻塞渲染的资源,所以浏览器将在 CSSOM 构建完毕前不会渲染任何已处理的内容。

  • JavaScript 不只能够读取和修改 DOM 属性,还能够读取和修改 CSSOM 属性,所以CSS解析与script的执行互斥。

正是因为以上这些缘由,script标签的位置很重要咱们在实际开发中应该尽可能坚持如下两个原则:

  • 在引入顺序上,CSS 资源先于 JavaScript 资源。

  • JavaScript 应尽可能少的去影响 DOM 的构建。

3.2 回流和重绘(reflow和repaint)

咱们都知道HTML默认是流式布局的,但CSS和JS会打破这种布局,改变DOM的外观样式以及大小和位置。所以咱们就须要知道两个概念:

  • reflow(回流):当浏览器发现某个部分发生了变化从而影响了布局,这个时候就须要倒回去从新渲染,你们称这个回退的过程叫 reflow。 常见的reflow是一些会影响页面布局的操做,诸如Tab,隐藏等。reflow 会从 html 这个 root frame 开始递归往下,依次计算全部的结点几何尺寸和位置,以确认是渲染树的一部分发生变化仍是整个渲染树。reflow几乎是没法避免的,由于只要用户进行交互操做,就势必会发生页面的一部分的从新渲染,且一般咱们也没法预估浏览器到底会reflow哪一部分的代码,由于他们会相互影响。

  • repaint(重绘): repaint则是当咱们改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,可是元素的几何尺寸和位置没有发生改变。

须要注意的是,display:none 会触发 reflow,而visibility: hidden属性则并不算是不可见属性,它的语义是隐藏元素,但元素仍然占据着布局空间,它会被渲染成一个空框,这在咱们上面有提到过。因此visibility:hidden 只会触发 repaint,由于没有发生位置变化。

咱们不能避免reflow,但仍是能经过一些操做来减小回流:

  1. 用transform作形变和位移.

  2. 经过绝对位移来脱离当前层叠上下文,造成新的Render Layer。

另外有些状况下,好比修改了元素的样式,浏览器并不会马上reflow 或 repaint 一次,而是会把这样的操做积攒一批,而后作一次 reflow,这又叫异步 reflow 或增量异步 reflow。可是在有些状况下,好比resize 窗口,改变了页面默认的字体等。对于这些操做,浏览器会立刻进行 reflow。

3.3 几条关于优化渲染效率的建议

结合上文和我看到的一些文章,有如下几点能够优化渲染效率

  1. 合法地去书写 HTML 和 CSS ,且不要忘了文档编码类型。

  2. 样式文件应当在 head 标签中,而脚本文件在 body 结束前,这样能够防止阻塞的方式。

  3. 简化并优化CSS选择器,尽可能将嵌套层减小到最小。

  4. 尽可能减小在 JavaScript 中进行DOM操做。

  5. 修改元素样式时,更改其class属性是性能最高的方法。

  6. 尽可能用 transform 来作形变和位移