浏览器渲染页面的原理

186 阅读4分钟

v2-b0bc0b83c73cf0de7cc86ae8c05027d9_1440w.png

我们先来了解一下浏览器是如何对网页进行渲染的:
  1. 浏览器将从服务器获取的HTML文档构建成文档对象模型DOM(Document Object Model).
  2. 样式将被载入和解析,构成层叠样式表模型CSSOM(CSS Object Model).
  3. 在DOM和CSSOM之上,渲染树(rendering tree)将会被创建,代表一系列将被渲染的对象(这在Webkit内核中被称为renderer或者渲染对象render object,在Gecko内核中被称为框架frame)。渲染树映射除了不可见元素(例如 "head"或者含有display:none; 的标签)外的所有DOM结构。每一段文本字符串都将划分在不同的渲染对象中,每一个渲染对象都包含了它相应的DOM对象以及计算后的样式。换句话讲,渲染树是DOM的直观表示。
  4. 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout.浏览器使用一种流式处理的方法,只需要一次pass绘制操作就可以布局所有的元素(tables需要多次pass绘制,pass表示像素处理和顶点处理)。
  5. 最后布局完成,渲染树将转化为屏幕上的实际内容,这一步被称为绘制painting.
回流和重绘都会影响浏览器渲染的
reflow(回流)

DOM结构中的各个元素都有自己的盒子模型,浏览器根据各样式计算元素的尺寸和位置,构建渲染树的过程称之为reflow。当渲染树节点的尺寸,布局,隐藏属性发生改变时,会触发reflow操作,重新构建Render Tree。每个页面在第一次加载的时候,会发生一次回流。 完成回流后,浏览器会重新绘制受影响的部分到屏幕中,因此,回流必定会导致重绘。

当改变影响文档内容或者结构,或者元素位置时,回流操作就会被触发,一般有以下几种情况:

  • DOM操作(对元素的增删改,顺序变化等);
  • 内容变化,包括表单区域内的文本改变,修改网页的默认字体;
  • CSS属性的更改或重新计算;
  • 绘制动画
  • 增删样式表内容;
  • 修改class属性;
  • 浏览器窗口变化(滚动或缩放);
  • 伪类样式激活( :hover等)。
  • 注:display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发现位置变化
repain(重绘)

当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器便把这些元素都按照各自的特性进行绘制,于是页面的内容出现了,这个过程称之为repaint。

如何能将Reflow对性能的影响减到最小呢?

1.不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。 2.把 DOM 离线后修改。如: 使用 documentFragment 对象在内存里操作 DOM; 先把 DOM 设置为 display:none (有一次 repaint),然后你想怎么改就怎么改。比如修改 100 次,然后再把他显示出来;克隆一个DOM 节点到内存里,然后想怎么改就怎么改,改完后,和在线的进行交换。 3.千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。 4.通过内联 JavaScript、内联 CSS 来移除这两种类型的文件下载,这样获取到 HTML 文件之后就可以直接开始渲染流程了。但并不是所有的场合都适合内联,那么还可以尽量减少文件大小,比如通过 webpack 等工具移除一些不必要的注释,并压缩 JavaScript 文件。 5.还可以将一些不需要在解析 HTML 阶段使用的 JavaScript 标记上 sync 或者 defer。 6.对于大的 CSS 文件,可以通过媒体查询属性,将其拆分为多个不同用途的 CSS 文件,这样只有在特定的场景下才会加载特定的 CSS 文件。