React16源码: React中FiberRoot的源码实现
2024-01-07 22:39:19
关于 FiberRoot
1 )概述
- 在
ReactDOM.render
过程当中,创建了一个ReactRoot
的对象 - 这个
ReactRoot
对象最主要承担了创建FiberRoot
对象 - 这个对象它非常重要,在后期整个应用调度过程当中都会跟它有关
- 关于
FiberRoot
对象- A. 它是整个应用的起点
- B. 它包含了
ReactDOM.render
传进去的第二个参数- 就是一个dom节点,我们应用挂载的那个节点相关的一些信息
- C. 它还记录着整个应用更新过程当中的各种信息
- 应用更新过程当中,会涉及到各种各样的东西
- 比如,各种不同类型的 expirationTime,还有异步调度任务的callback这些东
- 都会记录在这个 FiberRoot 上面
2 ) 相关源码
- 在 ReactRoot 构造函数中基于
DOMRenderer.createContainer
创建了FiberRoot
对象// ReactDOM.js function ReactRoot( container: Container, isConcurrent: boolean, hydrate: boolean, ) { const root = DOMRenderer.createContainer(container, isConcurrent, hydrate); this._internalRoot = root; } // ReactFiberReconciler.js export function createContainer( containerInfo: Container, isConcurrent: boolean, hydrate: boolean, ): OpaqueRoot { return createFiberRoot(containerInfo, isConcurrent, hydrate); } // ReactFiberRoot.js export function createFiberRoot( containerInfo: any, isConcurrent: boolean, hydrate: boolean, ): FiberRoot { // Cyclic construction. This cheats the type system right now because // stateNode is any. const uninitializedFiber = createHostRootFiber(isConcurrent); let root; if (enableSchedulerTracing) { root = ({ // root 节点对应的Fiber对象(root节点也是可能会更新的),是 Root Fiber // 后续整个应用都会有一个Fiber的树结构,任何一个ReactElement的节点都会对应一个Fiber对象 // ReactElement是一个树结构, Fiber对象也会有一个树结构 // 它是Fiber树结构的顶点 current: uninitializedFiber, containerInfo: containerInfo, // ReactDOM.render 传进去的第二个参数 dom 节点 pendingChildren: null, // 只有在持久更新中会用到,也就是不支持增量更新的平台,react-dom中不会被用到,在server-render中会被用到,因为它需要输出很多字符串类型的html节点 // 以下的优先级是用来区分 // 1 ) 没有提交(committed)的任务 // 2 ) 没有提交的挂起任务 // 3 ) 没有提交的可能被挂起的任务 // 最老和最新的不确定是否会挂起的优先级 (所有任务进来一开始都是这个状态) earliestPendingTime: NoWork, latestPendingTime: NoWork, // 最老和最新的在提交的时候被挂起的任务 earliestSuspendedTime: NoWork, latestSuspendedTime: NoWork, // 最新的通过一个promise被resolve并且可以重新尝试的优先级 latestPingedTime: NoWork, didError: false, // 在renderRoot 出现无法处理的错误时会被设置为 true pendingCommitExpirationTime: NoWork, // 正在等待提交的任务的 expirationTime // 已经完成的任务的FiberRoot对象,如果你只有一个Root, 它永远只可能是这个Root对应的Fiber, 或者是 null // 在 commit 阶段只会处理这个值对应的任务 // 用于记录在一次更新渲染过程当中完成了的更新任务, 因为整棵树中会存在各种不同的更新任务 // 每一次更新会渲染优先级最高的任务,优先级最高的任务渲染完成之后就是一个 finishedWork,标记在应用的Root上面 // 更新完了要把应用输出到Dom节点上面,输出的过程就是读取的这个 finishedWork 值 finishedWork: null, // 在任务被挂起的时候通过setTimeout设置的返回内容 // 用来下一次如果有新的任务挂起时清理还没触发的timeout // 例如:Suspense 组件功能, 在render function 中 throw 一个 Promise, 之后任务会被挂起 // 挂起之后可以渲染 Suspense 组件的 fallback, 等到 Promise resolve之后 // 它就会把 resolve之后的数据显示出来,这时候就会有一个 timeoutHandle来帮助我们记录超时的情况的 // 这个会在后续的更新流程中 timeoutHandle: noTimeout, // 顶层 context 对象, 只有主动调用 `renderSubtreeIntoContainer` 时才会有用 // `renderSubtreeIntoContainer` 这个API出场率非常低,不常用 context: null, pendingContext: null, // 用来确定第一次渲染的时候是否需要融合 // 应用是否要跟原来存在的dom节点进行合并 hydrate, // 当前root上剩余的过期时间 // 用来标记这一次更新渲染的时候要执行的是哪个优先级的任务 // 应用更新过程中会遍历到每一个节点,每个节点如果有更新,会有自己的一个 ExpirationTime // root 上记录整个应用当中优先级最高的 ExpirationTime // 在更新的过程中,会根据这个nextExpirationTimeToWorkOn变量去进行一个更新 // 就是说如果遍历到某个节点,发现自己的 ExpirationTime 比这个值要大,则说明它优先级要低 // 且还轮不到它来更新,就可以进行一个跳过,这就是这个变量的用处 nextExpirationTimeToWorkOn: NoWork, // 当前更新对应的过期时间 // 用在我们调度的过程当中, 和 上面的 nextExpirationTimeToWorkOn 大部分时间都是相同的 // 但是也会有一定的区别 expirationTime: NoWork, // 暂时不管这个 firstBatch: null, // root之间关联的链表结构 // 这个属性是单向链表的属性 // 比如,dom节点有 root1 和 root2 两个div // ReactDOM.render 把一个应用渲染在 root1 中,把另一个应用渲染在 root2 中 // 这时候会存在两个root, 它们在react中会用 nextScheduledRoot 这个属性进行一个串联 // 在整个react更新调度过程中,在这个链表中去找哪个root对应的优先级最高,执行那个root的更新 // 一个root里面又会有不同优先级的任务在里面,它整体的过程是非常的复杂 nextScheduledRoot: null, interactionThreadID: unstable_getThreadID(), memoizedInteractions: new Set(), pendingInteractionMap: new Map(), }: FiberRoot); } else { root = ({ current: uninitializedFiber, containerInfo: containerInfo, pendingChildren: null, earliestPendingTime: NoWork, latestPendingTime: NoWork, earliestSuspendedTime: NoWork, latestSuspendedTime: NoWork, latestPingedTime: NoWork, didError: false, pendingCommitExpirationTime: NoWork, finishedWork: null, timeoutHandle: noTimeout, context: null, pendingContext: null, hydrate, nextExpirationTimeToWorkOn: NoWork, expirationTime: NoWork, firstBatch: null, nextScheduledRoot: null, }: BaseFiberRootProperties); } uninitializedFiber.stateNode = root; // The reason for the way the Flow types are structured in this file, // Is to avoid needing :any casts everywhere interaction tracing fields are used. // Unfortunately that requires an :any cast for non-interaction tracing capable builds. // $FlowFixMe Remove this :any cast and replace it with something better. return ((root: any): FiberRoot); }
3 )相关解析
- 注意,上面的各种 Time 非常重要,在后续任务调度的过程中,用来记录更新所涉及的时间的
- js 在浏览器中是单线程的, 我们的应用又是一个树形结构
- 如何去区分不同优先级的任务,就要用各种不同的变量去标记
- 这几个变量就是用于去标识不同优先级的任务的
- 其他字段参考上述源码中注释的释义
文章来源:https://blog.csdn.net/Tyro_java/article/details/135445047
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!