第十二章 React的同步渲染、并发渲染(React18源码解析十二)

264 阅读3分钟

第一章 JSX(React18源码解析一)
第二章 实现Virtual DOM(React18源码解析二)
第三章 根节点关联根Fiber(React18源码解析三)
第四章 初始化UpdateQueue、添加update(React18源码解析四)
第五章 实现时间切片函数和拷贝Fiber(React18源码解析五)
第六章 构建FiberTree(React18源码解析六)
第七章 commit(React18源码解析七)
第八章 React中的事件(React18源码解析八)
第九章 useReducer、useState(React18源码解析九)
第十章 React的dom diff(React18源码解析十)
第十一章 React的Effect(React18源码解析十一)
第十二章 React的同步、并发渲染(React18源码解析十二)
第十三章 React高优打断低优、饥饿问题(React18源码解析十三)

react分片

分片即把fiber tree的构建过程从一次性构建完毕变为可中断、可恢复的多次构建完毕,因此可根据构建方式的不同分为同步渲染、并发渲染

lane模型

lane模型是指react中把各种情况下的setState划分了31条轨道,其目的是为了区分优先级,在第五章已叙述了使用MessageChannel和最小堆实现切片的函数,lane模型可根据当前setState所属轨道确定当前setState的过期时间,过期时间越近优先级越高,根据过期时间确定当前setState在最小堆中的排序,从而实现优先级高的setState的优先执行

同步渲染和并发渲染详解

首先在ensureRootIsScheduled中获取当前fiber.pendingLanes上优先级最高的lane,中判断当前setState的lane是否是SyncLane
1.若是SyncLane则调用scheduleImmediateTask(processRootScheduleInMicrotask)层层往下调用到performSyncWorkOnRoot,会一次性的构建完毕fiber tree,而后调用commit,此为同步渲染,此步骤可参照第六章
2.若不是SyncLane,会先把Lane转成EventPriority,再根据EventPriority确定schedulerPriorityLevel,调用scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root)) 创建task添加到最小堆中,根据schedulerPriorityLevel和当前时间计算出task的过期时间,task为callback的performConcurrentWorkOnRoot,根据task的过期时间在最小堆中排序,用MessageChannel做的分片函数会自动调用performConcurrentWorkOnRoot,performConcurrentWorkOnRoot会判断最高优先级的lane是否包含SyncLane、task是否过期,若包含SyncLane或task过期则调用renderRootSync则还是同步渲染,若不包含SyncLane且不过期则调用renderRootConcurrent,renderRootConcurrent会调用workLoopConcurrent循环构建fiber tree,不过相比同步渲染调用的workLoopSync多了分片函数执行过期的判断,过期则跳出while循环即打断fiber tree的构建,并且performConcurrentWorkOnRoot返回performConcurrentWorkOnRoot给task.callback,并以此标识当前task未执行完,不把当前task弹出执行下一个task,等下一帧执行调用MessageChannel时继续执行此task,即执行此task.callback,即接上上次中断的fiber tree继续构建,当fiber tree构建完成renderRootConcurrent返回status RootInProgress,表示完成fiber tree构建调用commit,此即为并发调用

同步渲染和并发渲染函数调用

//关键调用函数,因代码实在太多就不贴了,只记录下关键调用函数顺序
//react-main/packages/react-reconciler/src/ReactFiberWorkLoop.js
1.scheduleUpdateOnFiber=> 
//react-main/packages/react-reconciler/src/ReactFiberRootScheduler
2.ensureRootIsScheduled=>
    3.1.scheduleImmediateTask(processRootScheduleInMicrotask)=>
        4.flushSyncWorkOnAllRoots=>
        5.flushSyncWorkAcrossRoots_impl=>
        //react-main/packages/react-reconciler/src/ReactFiberWorkLoop.js
        6.performSyncWorkOnRoot=>
        7.renderRootSync=>
        8.workLoopSync
    3.2.scheduleTaskForRootDuringMicrotask
        //react-main/packages/react-reconciler/src/ReactFiberWorkLoop.js
        4.performConcurrentWorkOnRoot=>
        5.shouldTimeSlice
            ?
            renderRootConcurrent(root, lanes)=>
            6.workLoopConcurrent
            :
            renderRootSync(root, lanes)=>
            6.workLoopSync