react底层原理
https://juejin.cn/post/7211072055780573221#heading-0
https://juejin.cn/post/6926432527980691470
作为架构来说,之前React15
的Reconciler
采用递归的方式执行,数据保存在递归调用栈中,所以被称为stack Reconciler
。React16
的Reconciler
基于Fiber节点
实现,被称为Fiber Reconciler
。
新版React架构分为三大部分:
Scheduler调度器: 排序优先级,让优先级高的任务先进行reconcile
Reconciler协调器:接受更新,创建虚拟dom树,找出哪些节点发生了改变,并打上不同的effectTag
Renderer渲染器:将Reconciler中打好标签的节点渲染到视图上
Fiber这种数据结构后,能完成哪些事情呢,
工作单元 任务分解 :Fiber最重要的功能就是作为工作单元,保存原生节点或者组件节点对应信息(包括优先级),这些节点通过指针的形似形成Fiber树
增量渲染:通过jsx对象和current Fiber的对比,生成最小的差异补丁,应用到真实节点上
根据优先级暂停、继续、排列优先级:Fiber节点上保存了优先级,能通过不同节点优先级的对比,达到任务的暂停、继续、排列优先级等能力,也为上层实现批量更新、Suspense提供了基础
保存状态:因为Fiber能保存状态和更新的信息,所以就能实现函数组件的状态更新,也就是hooks
2. render阶段
render:Reconciler工作的阶段被称为render阶段。因为在该阶段会调用组件的render方法
render阶段的主要工作是构建Fiber树和生成effectList
开始工作前会先找到div#root对应的rootFiber,称为
hostRootFiber,然后开始生成
wip Fiber树。 这个过程分为两个部分:beginWork
和completeWork。 这是一个深度优先遍历的过程。
beginwork:主要的工作是创建或复用子fiber节点(同时涉及到diff算法,为结点打上effectTags)
从根节点rootFiber开始,遍历到叶子节点,每次遍历到的节点都会执行beginWork,并且传入当前Fiber节点,然后创建或复用它的子Fiber节点,并赋值给workInProgress.child。
completework:主要工作是处理fiber的props、创建dom、创建effectList
1、(处理props将变化的部分賦值给workInProgerss.updateQueue)
2、mount时 调用createInstance创建dom,将后代dom节点插入刚创建的dom中
3、将effectTag的节点,加入到effectList中
当遍历到子节点后,会执行completeWork方法,执行完成之后会判断此节点的兄弟节点存不存在,如果存在就会为兄弟节点执行completeWork,当全部兄弟节点执行完之后,会向上回到父节点执行completeWork,直到rootFiber。
shouldYiled 方法就是判断待处理的任务队列有没有优先级更高的任务,有的话就先处理那边的 fiber,这边的先暂停一下。
3. commit阶段
commit:Renderer工作的阶段被称为commit阶段。commit阶段会把render阶段提交的信息渲染在页面上
遍历render阶段生成的effectList,effectList上的Fiber节点保存着对应的props变化。之后会遍历effectList进行对应的dom操作和生命周期、hooks回调或销毁函数。
commit阶段的主要工作(即Renderer的工作流程)分为三部分:
before mutation阶段(执行DOM操作前)
这个阶段 DOM 节点还没有被渲染到界面上去,过程中会触发 getSnapshotBeforeUpdate
,也会处理 useEffect
钩子相关的调度逻辑。
mutation阶段(执行DOM操作)
这个阶段负责 DOM 节点的渲染。在渲染过程中,会遍历 effectList,根据effectTag的不同,执行不同的 DOM 操作。
layout阶段(执行DOM操作后)
这个阶段处理 DOM 渲染完毕之后的收尾逻辑。比如调用
componentDidMount/componentDidUpdate
,调用useLayoutEffect
钩子函数的回调等。除了这些之外,它还会把 fiberRoot 的 current 指针指向 workInProgress Fiber 树。
4. render函数
legacy模式
render调用legacyRenderSubtreeIntoContainer,作用是
1、创建FiberRootNode和rootFiber节点, (调用createRootImpl,其会调用到createFiberRoot创建fiberRootNode,然后调用createHostRootFiber创建rootFiber)
2、调用updateContainer创建创建Update对象挂载到updateQueue的环形链表上,(然后执行scheduleUpdateOnFiber调用performSyncWorkOnRoot进入render阶段和commit阶段)
concurrent模式:
调用ReactDOMRoot.prototype.render执行updateContainer,调用updateContainer创建创建Update对象挂载到updateQueue的环形链表上,(然后scheduleUpdateOnFiber异步调度performConcurrentWorkOnRoot进入render阶段和commit阶段)
5. fiber
Fiber是一个js对象,能承载节点信息、优先级、updateQueue,同时它还是一个工作单元。
工作单元 任务分解 :Fiber最重要的功能就是作为工作单元,保存原生节点或者组件节点对应信息(包括优先级),这些节点通过指针的形似形成Fiber树
增量渲染:通过jsx对象和current Fiber的对比,生成最小的差异补丁,应用到真实节点上。(
fiber
将react
中的渲染任务拆分到每一帧)根据优先级暂停、继续、排列优先级:Fiber节点上保存了优先级,能通过不同节点优先级的对比,达到任务的暂停、继续、排列优先级等能力,也为上层实现批量更新、Suspense提供了基础
保存状态:因为Fiber能保存状态和更新的信息,所以就能实现函数组件的状态更新,也就是hooks
6. hooks的实现原理
在函数式组件中,hooks 的实现就是基于 fiber 的,多个hook会形成hook链表,保存在Fiber的memoizedState的上。hook不能写在条件判断中正因为hook会按顺序存储在链表中,如果hook写在条件判断中,就没法保持链表的顺序,会造成乱序。
hook调用入口
在hook源码中hook存在于Dispatcher中,Dispatcher就是一个对象,不同hook 调用的函数不一样,全局变量ReactCurrentDispatcher.current会根据是mount还是update赋值为HooksDispatcherOnMount或HooksDispatcherOnUpdate。
usestate的工作原理
useState分为onMount和upDate两种情况,通过全局变量ReactCurrentDispatcher.current来判断。
onMount时,hook.memoizedState记录初始的值
update更新时:
其中hooks的memoizedState
是用来记录这个useState
应该返回的结果的,而next
指向的是下一次useState
对应的`Hook对象。
memoizedState装着state,而dispatchAction 就是setState
dispatchAction 创建新的update对象,将这些更新对象放到hook.queue.pending的环形链表中,最后重新渲染app
hooks的数据结构
const hook: Hook = { memoizedState: null,//对于不同hook,有不同的值 baseState: null,//初始state baseQueue: null,//初始queue队列 queue: null // { pending:这是一个链表储存着update},//需要更新的update next: null,//下一个hook };
评论
发表评论