什么是Reconciliation?
在 React 的官网在解释 virtualDom 时,有这样一段话:
"The virtual DOM (VDOM) is a programming concept where an ideal, or “virtual”, representation of a UI is kept in memory and synced with the “real” DOM by a library such as ReactDOM. This process is called reconciliation."
Reconciliation 是实现UI更新的一个过程。这里翻译成“调和”;思考:为什么叫调和??
调和的目的
Reconciliation 的目的是在用户无感知的情况下将数据的更新体现到UI上。
触发调和过程的方式
在React 中有以下几种操作会触发调和过程:
1、ReactDom.render() 函数和 ReactNativeRenderer.render() 函数
2、setState() 函数
2、forceUpdate() 函数的调用
4、componentWillMount 和 componentWillReceiveProp 中直接修改了state(地址)
5、hooks 中的 useReducer 和 useState 返回的钩子函数
调和过程涉及的数据结构
接触 React 时,首先接触的是 virtualDom 这个词,会好奇什么是 virtualDom。开端的引文已经解释,virtualDom 只是一种编程理念,在 react 中若一定要将他与某种数据相关联,那应该是 ReactElement 和 Fiber,Fiber 更合适。
react15 在 render 阶段的 reconcile 是不可打断的,这会在进行大量节点的 reconcile 时可能产生卡顿,因为浏览器所有的时间都交给了js执行,并且 js 的执行时单线程。为此 react16 之后就有了 scheduler 进行时间片的调度,给每个 task(工作单元)一定的时间,如果在这个时间内没执行完,也要交出执行权给浏览器进行绘制和重排,所以异步可中断的更新需要一定的数据结构在内存中来保存工作单元的信息,这个数据结构就是 Fiber。
那么有了 Fiber 这种数据结构后,能完成哪些事情呢?
工作单元 任务分解 :Fiber 最重要的功能就是作为工作单元,保存原生节点或者组件节点对应信息(包括优先级),这些节点通过指针的形似形成 Fiber 树。
增量渲染:通过 jsx 对象和 current Fiber 的对比,生成最小的差异补丁,应用到真实节点上。
根据优先级暂停、继续、排列优先级:Fiber 节点上保存了优先级,能通过不同节点优先级的对比,达到任务的暂停、继续、排列优先级等能力,也为上层实现批量更新、Suspense 提供了基础保存状态:因为 Fiber 能保存状态和更新的信息,所以就能实现函数组件的状态更新,也就是 hooks。
Fiber 的数据结构,Fiber 的自带的属性如下:
//ReactFiber.old.js
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
//作为静态的数据结构 保存节点的信息
this.tag = tag;//对应组件的类型
this.key = key;//key属性
this.elementType = null;//元素类型
this.type = null;//func或者class
this.stateNode = null;//真实dom节点
//作为fiber数架构 连接成fiber树
this.return = null;//指向父节点
this.child = null;//指向child
this.sibling = null;//指向兄弟节点
this.index = 0;
this.ref = null;
//用作为工作单元 来计算state
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
//effect相关
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
//优先级相关的属性
this.lanes = NoLanes;
this.childLanes = NoLanes;
//current和workInProgress的指针
this.alternate = null;
}