React 类组件源码浅析 #110
zhangyu1818
announced in
zh-cn
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
虽然现在都是使用函数组件,但是类组件还是有函数组件无法替代的地方,比如一些独有的生命周期、很方便的存一些变量而不需要一大堆
useRef
、实例的方法调用更方便。以下源码浅析React版本为17.0.1。
类组件的Fiber节点的创建
类组件的Fiber节点由父级节点进入
reconcileChildren
方法后通过createFiberFromTypeAndProps
方法创建,在这个方法中会通过prototype.isReactComponent
判断该节点是否为ClassComponent
。由于类组件需要继承
Component
,所有存在prototype.isReactComponent
,该节点被标记为类组件,后续会进入类组件的判断。类组件实例的创建和更新
上一步标记了该Fiber节点为
ClassComponent
,该节点进入beginWork
阶段时会通过判断进入updateClassComponent
方法。无论类组件是首次挂载还是更新,都会进入
updateClassComponent
。updateClassComponent
中会针对Mount和Update分开判断。Mount时
Mount时会创建实例,赋值
updater
(也就是setState
和forceUpdate
等),初始化updateQueue
。主要调用了constructClassInstance方法和
mountClassInstance`方法。constructClassInstance
constructClassInstance
方法主要创建类实例并绑定到Fiber节点,赋值updater
。mountClassInstance
mountClassInstance
方法会对类实例的props
、state
赋值,并初始化updateQueue
,同时执行getDerivedStateFromProps
生命周期获取新的state
,同时根据判断执行componentWillMount
生命周期。后续
state
的值会通过执行updateQueue
的中的update
来更新。Update时
Update时调用
updateClassInstance
来处理更新。updateClassInstance
finishClassComponent
Mount和Update后都需要执行
finishClassComponent
方法,该方法会返回经过reconcileChildren
处理后的child
节点。类组件的更新调度
类的更新存在于Fiber节点上的
updateQueue
,上面存储了Update
的信息,类组件可以通过setState
创建Update
并添加到updateQueue
发起更新调度,在Update时执行updateClassInstance
方法时会调用processUpdateQueue
方法计算新的state
。UpdateQueue和Update的结构
在类组件Mount时执行
mountClassInstance
方法时,内部会调用initializeUpdateQueue
方法初始化updateQueue
。通过执行
setState
会调用createUpdate
方法创建update
,并添加到updateQueue
中,Update
是一个环形链表,next
字段指向下一个Update
。processUpdateQueue
在类组件Update时会调用
processUpdateQueue
方法中会执行updateQueue
来获得新的state
。processUpdateQueue
方法里会执行updateQueue
计算新的state
值,主要是其中包含了优先级以及跳过的Update
的判断有一些障目。主要逻辑还是循环Update
链表来获得新的state
。state
值会通过getStateFromUpdate
方法来获得。getStateFromUpdate
getStateFromUpdate
函数内会根据Update
类型来处理state
,这里主要关注由setState
发起的UpdateState
类型。setState和forceUpdate
setState
方法和forceUpdate
方法是继承Component
后原型链上的方法,本质是调用的在Mount时赋值当this.updater
。而
this.updater
是在Mount时在constructClassInstance
方法里调用adoptClassInstance
方法时赋值的。classComponentUpdater
updater
本质调用的就是classComponentUpdater
的方法。setState
和forceUpdate
主要区别是Update
的tag
类型不同,在执行updateQueue
通过Update
获取state
时,getStateFromUpdate
方法返回不同的结果。它们的回调函数会在后续的commit阶段执行。
类组件生命周期的执行
以下生命周期不包含未来不推荐使用的
UNSAFE
生命周期,省略了部分无关代码。render阶段
在未来开启
concurrent
模式后,目前UNSAFE
的生命周期可能会因为渲染中断或优先级的高低执行多次,虽然目前不会有任何问题,但还是应该尽量避免使用。constructor
constructor
在执行的时候实际上会传入context
作为第二个参数,这个在文档上是没有的。shouldComponentUpdate
shouldComponentUpdate
在Update时通过updateClassInstance
方法里每次都会调用。getDerivedStateFromProps
getDerivedStateFromProps
就像文档写的会在每次渲染前触发此方法,它在Mount时和Update时都会调用,通过执行函数获取一个新的state
值,并且会合并到当前state
上,同时会维护某些情况下updateQueue
的baseState
值。componentDidMount
虽然在render阶段不会执行
componentDidMount
,但是它会在render
阶段打上flags
,后续commit
阶段才会执行。componentDidMount和getSnapshotBeforeUpdate
和
componentDidMount
相同,在render
阶段需要打上flags
,但是会判断props
和state
有没有变化。commit阶段
commit阶段又分为3个阶段
BeforeMutation阶段
BeforeMutation阶段在操作DOM前。
getSnapshotBeforeUpdate
通过
current
判断是否为Update时,并执行getSnapshotBeforeUpdate
方法,按照文档的描述,这个生命周期可以做滚动条的位置,因为这时候获取DOM的值还是旧的。Mutation阶段
Mution阶段正在对DOM进行操作。
componentWillUnMount
当Fiber节点
flags
包含Deletion
时,最后会进入commitUnmount
方法,由safelyCallComponentWillUnmount
方法调用componentWillUnMount
。Layout阶段
Layou阶段是操作DOM后。
componentDidMount和componentDidMount
componentDidMount
和componentDidMount
在同一个函数中执行,通过current === null
判断是Mount还是Update来确定执行哪一个函数。总结
以上简单从源码简单分析了类组件Fiber节点创建、类组件实例创建、更新,发起更新调度,生命周期的执行,回头再对比Hooks,我觉得某些情况类组件真的还挺方便,虽然大部分功能函数组件都能实现,但是还是的花一些时间使用Hooks来实现对应功能。
Beta Was this translation helpful? Give feedback.
All reactions