React原理

242 阅读5分钟

react原理解析

react分为render阶段和commit阶段

render阶段

render阶段的主要工作是构建Fiber树和生成effectList

performSyncWorkOnRoot或者performConcurrentWorkOnRoot是render阶段的开始标志,内部调用performUnitOfWork函数

performUnitOfWork进行深度优先遍历将已创建的fiber节点连接,构建workInProgress树。

render阶段.png

render阶段又分为两个阶段,beginWork(捕获阶段)和completeWork(冒泡阶段)。

beginwork 捕获阶段

从根节点rootFiber开始,遍历到叶子节点,每次遍历到的节点都会执行beginWork函数,并且传入当前Fiber节点,然后创建或复用它的子Fiber节点,并赋值给workInProgress.child。beginwork函数主要的工作是创建或复用子fiber节点。

diff算法

diff算法是发生在render阶段的beginwork阶段调用的reconcileChildFibers函数中,通过对比current Fiber和jsx对象构建workInProgress Fiber。 该函数根据newChild的类型来进入单节点的diff或者多节点diff。

react为了降低复杂度,提出了三个前提:

  • 只对同级比较,跨层级的dom不会进行复用
  • 不同类型节点生成的dom树不同,此时会直接销毁老节点及子孙节点,并新建节点
  • 可以通过key来对元素diff的过程提供复用的线索

单节点diff

单点diff有如下几种情况:

  • key和type相同表示可以复用节点
  • key不同直接标记删除节点,然后新建节点
  • key相同type不同,标记删除该节点和兄弟节点,然后新创建节点

completework 冒泡阶段

在捕获阶段遍历到子节点之后,会执行completeWork方法,执行完成之后会判断此节点的兄弟节点存不存在,如果存在就会为兄弟节点执行completeWork函数,当全部兄弟节点执行完之后,会向上‘冒泡’到父节点执行completeWork,直到rootFiber,最后调用commitRoot函数进入commit阶段。

completework函数主要工作是处理fiber的props、创建dom、创建effectList,并将beginwork阶段被打上effectTag的节点存入effectList中,方便commit阶段的遍历执行(以空间换时间),这一过程发生completeUnitOfWork函数中,其作用就是向上合并effectList。

commit阶段

调用commitRoot(root)进入commit阶段,这里的root指的就是fiberRoot,然后会遍历render阶段生成的effectList,effectList上的Fiber节点保存着对应的props变化。之后会遍历effectList进行对应的dom操作和生命周期、hooks回调或销毁函数。

commit阶段.png

commit阶段分为三个阶段,分别是pre-commit,mutation阶段,mutation后

pre-commit

  • 调用flushPassiveEffects执行完所有effect的任务
  • 初始化相关变量
  • 赋值firstEffect给后面遍历effectList用

mutation阶段

遍历effectList分别执行三个方法commitBeforeMutationEffects、commitMutationEffects、commitLayoutEffects执行对应的dom操作和生命周期

  1. commitBeforeMutationEffects该函数主要做了如下两件事
  • 执行getSnapshotBeforeUpdate

    因为commit阶段是同步的,所以getSnapshotBeforeUpdate也同步执行

  • 调度useEffect

    componentDidUpdate或componentDidMount会在commit阶段同步执行,而useEffect会在commit阶段异步调度,所以适用于数据请求等副作用的处理。

  1. commitMutationEffects
  • 调用commitDetachRef解绑ref
  • 根据effectTag执行对应的dom操作
  • useLayoutEffect销毁函数在UpdateTag时执行
  1. commitLayoutEffects

    在commitMutationEffects之后所有的dom操作都已经完成,可以访问dom了,commitLayoutEffects主要做了以下两件事

  • 调用commitLayoutEffectOnFiber执行相关生命周期函数或者hook相关callback

    在源码中commitLayoutEffectOnFiber函数的别名是commitLifeCycles,在简化后的代码中可以看到,commitLifeCycles会判断fiber的类型,SimpleMemoComponent会执行useLayoutEffect的回调,然后调度useEffect,ClassComponent会执行componentDidMount或者componentDidUpdate,this.setState第二个参数也会执行,HostRoot会执行ReactDOM.render函数的第三个参数。

    在flushPassiveEffects函数中调用flushPassiveEffectsImpl遍历pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount,执行对应的effect回调和销毁函数,而这两个数组是在commitLayoutEffects函数中赋值的,mutation后effectList赋值给rootWithPendingPassiveEffects,然后scheduleCallback调度执行flushPassiveEffects。

    所以useEffect是异步执行,而useLayoutEffect是同步执行。

  • 执行commitAttachRef为ref赋值

    commitAttacRef中会判断ref的类型,执行ref或者给ref.current赋值

mutation后

  • 根据rootDoesHavePassiveEffects赋值相关变量
  • 执行flushSyncCallbackQueue处理componentDidMount等生命周期或者useLayoutEffect等同步任务

各阶段生命周期执行情况

  1. render阶段:
  • mount时:constructor、getDerivedStateFromProps、render
  • update时:getDerivedStateFromProps、shouldComponentUpdate、render
  • error时:调用getDerivedStateFromError
  1. commit阶段
  • mount时:componnetDidMount
  • update时:调用getSnapshotBeforeUpdate、componnetDidUpdate
  • unMount时:调用componnetWillUnmount
  • error时:调用componnetDidCatch

class组件的生命周期.png

在react中触发状态更新的几种方式:

  • ReactDOM.render
  • this.setState
  • this.forceUpdate
  • useState
  • useReducer

update流程.png

hook

hook存在于Dispatcher中,Dispatcher就是一个对象,不同hook 调用的函数不一样,全局变量ReactCurrentDispatcher.current会根据是mount还是update赋值为HooksDispatcherOnMount或HooksDispatcherOnUpdate

hook数据结构

在FunctionComponent中,多个hook会形成hook链表,保存在Fiber的memoizedState的上,而需要更新的Update保存在hook.queue.pending中

(待补充)

context

  • 在render阶段调用updateContextProvider的时候会执行pushProvider,将新的值push进valueStack中
  • 在commit阶段调用completeWork的时候会执行popProvider,将栈顶context pop出来,

为什么会有这样一个机制呢,因为我们的context是跨层级的,在之前讲到render阶段和commit阶段的时候,我们会以深度优先遍历的方式遍历节点,如果涉及跨层级读取状态就有点力不从心了,就需要一层一层往下传递我们的props,所以我们可以用一个stack记录我们的context,在render阶段pushProvider,在commit阶段popProvider,在每个具体的层级能根据valueCursor取当前value。

事件系统

react事件系统机制.png

文章参考链接:mp.weixin.qq.com/mp/homepage…