重流与重绘


###1.浏览器的内核
在过去,JS 还没充分地利用起来的时候,浏览器的内核包含了 JS 引擎 和渲染引擎,而现在浏览器内核只包含了渲染引擎,JS 引擎已经独立出来了(例如 V8)。所以现在我们所说的浏览器引擎一般是指渲染引擎。
现在比较的流行的引擎有:

内核 浏览器
Trident IE 系列
Gecko firefox
Webkit Safari,Android
blink Chrome,Opera

一般浏览器内核工作的流程如下图所示:
当浏览器就收到你的HTML文件和CSS文件时,触发过程是这样的.
内核运作

  • 首先Parse对文件进行解析
  • 然后将对应的HTML生成为DOM
  • CSS解析为CSS Object Model.
  • 然后两者合并进行render
  • 最后绘制到页面上

###2. JS引擎怎么工作的?
实际上,JS引擎要比浏览器引擎高一级。

  1. 获得文件时,浏览器开始解析文档
  2. 解析到script标签时,则会暂停解析,将控制权给JS引擎
  3. 如果script引用的是外部资源,则会发起请求进行加载,然后执行
  4. 执行完毕后再将控制权还给渲染引擎,然后继续解析。

但,就是由于浏览器会将控制权交给JS引擎,所以如果你的加载的资源过长,网页就死在哪里,一动也不动,直到你加载好为止。所以,通用的做法就是将js文件放到body底部,保证DOM树的完整渲染。

但是,实事情况并不是这么简单,源于JS优化加载这一块,也是有很多优化的点的。

另外,我们还需要掌握一个小tip. 浏览器最多能同时下载几个文件呢?

答案是,不确定,通常来说是6个,而IE11则是13个。这里的文件,不仅仅指js和css而是指,一切通过请求发送的,都算一份文件。所以,通常的做法就是,合并脚本,CDN优化,资源分布防止。

###3.什么是重流,重绘?
通常页面在加载你的js,css,img等文件时,引擎会对文件加以解析,最终生成两颗树,渲染树和DOM树. DOM树中的需要显示节点在渲染树中都会存在,但是display:none的则不会存在。 可以说,渲染树是指定DOM显示的真实节点,而DOM树则是页面显示的HTML结构。 在渲染树中,常常将节点成为帧或者盒子。这里,也可以理解为渲染树,其实就是css文件指定节点的样式表。

当渲染树和DOM树都已经完成的时候,则开始将页面显示到桌面上了。

这时候,如果你改变页面的DOM结构,浏览器则会重新改动涉及到的DOM. 此时你的渲染树和DOM树就会发生改变。

浏览器会重新计算出渲染树这一过程叫做重流(重排)。

将更新后的结构重新渲染到页面这一过程叫做重绘。

整个流程就是这个图
内核运作

DOM 操作非常之慢。
为什么呢?

重绘是一个耗时的过程,然而重流是一个更耗时的过程,我们无法确定重流一定是自上而下或是自下而上进行的,甚至一次重流会牵涉到整个文档布局的重新计算。

但是重流是肯定无法避免的,所以我们主要是要最小化重流的次数,reflow 这个操作是用来计算文档中元素的位置和大小,是渲染前重要的一步。在HTML第一次被加载的时候,会有一次 reflow 之外,js脚本的执行和样式的改变同样会导致浏览器执行 reflow ,这也是本文的主要要讨论的内容。

一般情况下,浏览器的 reflow 是 lazy 的,也就是说:在 js 脚本执行时,是不会去更新 DOM 的,任何对 DOM 的修改都会被暂存在一个队列中,在当前 js 的执行上下文完成执行后,会根据这个队列中的修改,进行一次 reflow。

然而有时希望在 js 代码中立刻获取最新的 DOM 节点信息,浏览器就不得不提前执行 reflow ,这是导致 DOM 性能问题的主因。

如下的操作会打破常规,并触发浏览器执行 reflow:

  1. 通过 js 获取需要计算的 DOM 属性
  2. 添加或删除 DOM 元素
  3. resize 浏览器窗口大小
  4. 改变字体
  5. css 伪类的激活,比如 :hover
  6. 通过 js 修改 DOM 元素样式且该样式涉及到尺寸的改变

React 引入了 虚拟 DOM 的概念:开发者操作虚拟 DOM,React 在必要的时候将它们渲染到真正的 DOM 上