React
在Web开发中,要将更新的数据实时反映到UI上,就不可避免地需要对DOM进行操作,而复杂频繁的DOM操作通常是产生性能瓶颈的原因之一。为此,React引入了Virtual DOM
机制。Virtual DOM
实际上是在浏览器端用JavaScript
实现的一套DOM API
,它包括:
-
Virtual DOM
模型 - 生命周期的维护和管理
- 性能高效的diff算法
- 将
Virtual DOM
展示为原生DOM的Patch
方法
基于React进行开发时,所有的DOM树都是在Virtual DOM
的基础上构造的。React在Virtual DOM
上实现了DOM diff
算法,当数据更新时,会通过diff算法寻找到需要变更的DOM节点,并只对变化的部分进行实际的浏览器的DOM更新,而不是重新渲染整个DOM树。
Virtual DOM
模型
ReactNode
ReactElement
- ReactComponentElement
- ReactDOMElement
- ReactFragment
- ReactText
创建React元素
通过JSX创建的虚拟元素最终会被编译成调用React的createElement
方法
初始化组件入口
当使用React创建组件时,首先会调用instantiateReactComponent
,这是初始化组件的入口函数,它通过判断node类型来区分不同组件的入口
文本组件
ReactDOMTextComponent
标签组件
ReactDOMComponent
自定义组件
ReactCompositeComponent
生命周期
React的主要思想是通过构建可复用组件来构建用户界面。所谓组件,就是有限状态机,通过状态渲染对应页面,每个组件组件都有自己的生命周期,它规定了组件和方法需要在哪个阶段改变和执行。
diff算法
diff算法会帮助我们计算出Virtual DOM
中真正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面,从而保证了每次操作更新后页面的高效渲染。
传统diff算法
- 计算一颗树形结构转换成另一棵树形结构的最少操作
- 传统算法通过循环递归对节点进行一次对比,算法复杂度达到O(n³)
React对diff算法的改进
React结合DOM树的特点,对传统diff算法进行了改进,将其转换为O(n)复杂度的问题
React diff算法的三个策略
- Web UI中DOM节点跨层级的操作较少(如果有,可以理解为删去一个节点,在另一层级插入新节点)
- 拥有相同类的两个组件会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
- 对于同一层级的一组子节点,它们可以通过唯一的id进行区分
tree diff(上面的第一个策略)
- 对树进行分层比较,两颗树只会对同一层次的节点进行比较
- React只会简单地考虑同层级节点的位置变换,而对于不同层级的节点,只有创建和删除操作
- 在开发组件时,保持稳定的DOM结构有助于性能的提升,建议不要进行DOM节点跨层级的操作
component diff(第二个策略)
- 如果是同一类型的组件,则按照原策略继续比较Virtual DOM
- 如果不是同一类型的组件,则将旧组件直接删除,在该位置重新创建新组件
- 如果是同一类型的组件,有可能其Virtual DOM没有任何变化,如果我们能够明确知道这点,这可以利用shouldComponentUpdate()来判断组件是否需要进行diff算法分析
element diff(第三个策略)
- 当节点处于同一层级时,diff算法提供了3中节点操作,分别为插入、移动、删除
- 对于同一层级的同组子节点,需要添加唯一key进行区分,通过这种方法来解决相同节点位置变化的情况
React Patch方法
将diff算法计算出来的DOM差异队列更新到真实的DOM节点上,最终让浏览器能够渲染出更新的数据。
(未完待续…)