React.js那些事(core篇)

这一篇主要讲讲React.js虚拟DOM的底层相关,主要是自己学习官方文档的Reconciliation部分的一些总结。我们都知道,React的虚拟DOM是非常高效的。那么,React底层是使用什么样的算法实现DOM的高效渲染呢?

机制

React.js在内存里的DOM树操作是子树的替换,也就是用变化之后的子树整个代替原来的子树,而不是在内存里面调整子树。从算法的角度来说,生成最少的将一颗树形结构转换成另一颗树形结构的操作,其最优算法的复杂度是 O(n3),n是树中节点的总数。所以React尝试从另一个方面入手,使用试探的方法。首先需要达成两点假设:
1、拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。
2、可以为元素提供一个唯一的标志,该元素在不同的渲染过程中保持不变。实际上,这些假设会使在几乎所有的应用场景下,应用变得出奇地快。

详细

diffing算法

为了实现子树的替换,首先必须要检查两棵树的差异。React的处理机制分三种类型处理。
示例1

不同的节点类型

节点类型不同,React 将会把它们当做两个不同的子树,移除之前的那棵子树,然后创建并插入第二棵子树。React不会尝试计算出该渲染什么,仅会从 DOM中移除之前的节点,然后插入新的节点。碰到不一样的组件结构,React将重新创建树形结构,而不是耗费时间去尝试匹配这两个树形结构。所以,React的虚拟DOM是非常高效的。

DOM节点

DOM节点是相同类型的,比较两个DOM节点的时候,首先查看属性。对随着时间发生了变化的属性进行更新,然后递归检查所有子级的属性。

自定义组件

我们决定两个自定义组件是相同的。因为组件是状态化的,不可能每次状态改变都要创建一个新的组件实例。React 利用新组件上的所有属性,然后在之前的组件实例上调用 component[Will/Did]ReceiveProps()。现在,之前的组件就是可操作了的。它的 render() 方法被调用,然后差异算法重新比较新的状态和上一次的状态。

子级优化差异

React采用的方法是同时遍历两个子级列表,当发现差异的时候,就产生一次 DOM 修改。例如我们如果需要在组件末尾添加一个元素,那么如果两个子级(原有的和新添加的)是相同元素,则直接修改已有子级元素的文本内容,然后在后面插入一个新的子级节点。
另外,React引入一个唯一的key属性,给每一个子级一个键值,用于和以后变化的组件进行比较。这样,React就能够检测出节点插入、移除和替换,并且借助哈希表使节点移动复杂度为 O(n)。
示例1

本篇结语

这一篇主要是介绍React的渲染机制,虚拟dom是React.js的核心。想要更多地了解React.js的朋友可以进入官方文档进行深入学习。