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
Uh oh!
There was an error while loading. Please reload this page.
-
虽然现在都是使用函数组件,但是类组件还是有函数组件无法替代的地方,比如一些独有的生命周期、很方便的存一些变量而不需要一大堆
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