前言
当state发生变化,react到底干了些啥
源自这样一个问题,让我产生了要读源码的想法。
state变化,对于react这个框架,分setState(类组件)和useState(hooks)两种方式。两种方式更新过程大体上一致,区别在于创建的update结构有些不同,useState创建的update有eagerState和hasEagerState,用于在调度之前做优化策略(涉及到双缓树的设计缺陷)(这句话需要串联的知识略多,新手可先跳过)。
这里以hooks写法的onClick事件举例:
import { Button } from 'antd';
import { useState } from 'react';
function Test() {
const [num, setNum] = useState<number>(0);
const handleNum = () => {
setNum(num + 1);
};
return (
<div onClick={handleNum}>
<div>{num}</div>
<Button>+</Button>
</div>
);
}
export default Test;
通过onClick触发handleNum事件react会找到触发当前事件的fiber节点,fiber的hooks链表对应的useState这个hook,调用此hook的dispatch方法(需要了解fiber,react事件系统,hooks结构),对应的是dispatchSetState方法:
function dispatchSetState<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
) {
// 计算当前事件的lane
const lane = requestUpdateLane(fiber);
// 创建update
const update: Update<S, A> = {
lane,
action,
hasEagerState: false,
eagerState: null,
next: (null: any),
};
// 判断是否是render阶段
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {
//`eagerState`优化策略针对的是单次触发状态修改的校验,`updateReducer`中最后的`Bailout`策略
//针对的是一个状态,经过`updateQueue`链表循环计算出最新的结果后,再次的校验状态是否发生变化,
//如果没有发生变化则进入`Bailout`策略。一个状态在一个事件回调被多次修改,就会将所有创建的`update`
//对象添加到此`hook`对象的`updateQueue`链表中
const alternate = fiber.alternate;
// 判断双缓冲树的lane是否被清除,涉及到react性能优化问题,相同状态render三次问题
if (
fiber.lanes === NoLanes &&
(alternate === null || alternate.lanes === NoLanes)
) {
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
let prevDispatcher;
try {
// 当前的state,即旧的state
const currentState: S = (queue.lastRenderedState: any);
// 快速计算最新的state
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
if (is(eagerState, currentState)) {
enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
return;
}
} catch (error) {
// Suppress the error. It will throw again in the render phase.
} finally {
//
}
}
}
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
const eventTime = requestEventTime();
// 开启调度任务
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
entangleTransitionUpdate(root, queue, lane);
}
}
markUpdateInDevTools(fiber, lane, action);
}
整体更新流程如下:
- 获取更新的优先级lane
- 创建update对象
- 将update对象加入到当前fiber的更新队列
- 开启调度任务
获取更新的优先级lane
react中存在三种优先级:
- lane优先级
- react事件优先级
- scheduler优先级
lane
lane优先级:
export const TotalLanes = 31;
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
export const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputContinuousLane: Lane = /* */ 0b0000000000000000000000000000100;
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000000001000;
export const DefaultLane: Lane = /* */ 0b0000000000000000000000000010000;
const TransitionHydrationLane: Lane = /* */ 0b0000000000000000000000000100000;
const TransitionLanes: Lanes = /* */ 0b0000000001111111111111111000000;
const TransitionLane1: Lane = /* */ 0b0000000000000000000000001000000;
const TransitionLane2: Lane = /* */ 0b0000000000000000000000010000000;
const TransitionLane3: Lane = /* */ 0b0000000000000000000000100000000;
const TransitionLane4: Lane = /* */ 0b0000000000000000000001000000000;
const TransitionLane5: Lane = /* */ 0b0000000000000000000010000000000;
const TransitionLane6: Lane = /* */ 0b0000000000000000000100000000000;
const TransitionLane7: Lane = /* */ 0b0000000000000000001000000000000;
const TransitionLane8: Lane = /* */ 0b0000000000000000010000000000000;
const TransitionLane9: Lane = /* */ 0b0000000000000000100000000000000;
const TransitionLane10: Lane = /* */ 0b0000000000000001000000000000000;
const TransitionLane11: Lane = /* */ 0b0000000000000010000000000000000;
const TransitionLane12: Lane = /* */ 0b0000000000000100000000000000000;
const TransitionLane13: Lane = /* */ 0b0000000000001000000000000000000;
const TransitionLane14: Lane = /* */ 0b0000000000010000000000000000000;
const TransitionLane15: Lane = /* */ 0b0000000000100000000000000000000;
const TransitionLane16: Lane = /* */ 0b0000000001000000000000000000000;
const RetryLanes: Lanes = /* */ 0b0000111110000000000000000000000;
const RetryLane1: Lane = /* */ 0b0000000010000000000000000000000;
const RetryLane2: Lane = /* */ 0b0000000100000000000000000000000;
const RetryLane3: Lane = /* */ 0b0000001000000000000000000000000;
const RetryLane4: Lane = /* */ 0b0000010000000000000000000000000;
const RetryLane5: Lane = /* */ 0b0000100000000000000000000000000;
export const SomeRetryLane: Lane = RetryLane1;
export const SelectiveHydrationLane: Lane = /* */ 0b0001000000000000000000000000000;
const NonIdleLanes: Lanes = /* */ 0b0001111111111111111111111111111;
export const IdleHydrationLane: Lane = /* */ 0b0010000000000000000000000000000;
export const IdleLane: Lane = /* */ 0b0100000000000000000000000000000;
export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
react事件优先级:
// 离散事件优先级,例如点击click,输入input,下拉select等事件,设为同步优先级,优先级别最高
export const DiscreteEventPriority: EventPriority = SyncLane; //
// 连续事件优先级,例如滚动scroll,拖拽drop等事件
export const ContinuousEventPriority: EventPriority = InputContinuousLane;
// 默认的优先级,例如setTimeout
export const DefaultEventPriority: EventPriority = DefaultLane;
// 闲置的事件,优先级最低
export const IdleEventPriority: EventPriority = IdleLane;
// lane转换为事件优先级
export function lanesToEventPriority(lanes: Lanes): EventPriority {
// 获取最高优先级
const lane = getHighestPriorityLane(lanes);
if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
return DiscreteEventPriority;
}
if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
return ContinuousEventPriority;
}
if (includesNonIdleWork(lane)) {
return DefaultEventPriority;
}
return IdleEventPriority;
}
scheduler优先级:
// 源码位置react\packages\scheduler\src\SchedulerPriorities.js
export const NoPriority = 0; //没有优先级
export const ImmediatePriority = 1; // 立即执行任务的优先级,级别最高
export const UserBlockingPriority = 2; // 用户阻塞的优先级
export const NormalPriority = 3; // 正常优先级
export const LowPriority = 4; // 较低的优先级
export const IdlePriority = 5; // 优先级最低,闲表示任务可以闲置
//
// react事件优先级转为scheduler优先级
// 在源码的ensureRootIsScheduled方法中
let schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediateSchedulerPriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdleSchedulerPriority;
break;
default:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}
获取当前事件的优先级
export function requestUpdateLane(fiber: Fiber): Lane {
// 获取到当前渲染的模式:sync mode(同步模式) 或 concurrent mode(并发模式)
const mode = fiber.mode;
if ((mode & ConcurrentMode) === NoMode) {
// 检查当前渲染模式是不是并发模式,等于NoMode表示不是,则使用同步模式渲染
return (SyncLane: Lane);
} else if (
!deferRenderPhaseUpdateToNextBatch &&
(executionContext & RenderContext) !== NoContext &&
workInProgressRootRenderLanes !== NoLanes
) {
// workInProgressRootRenderLanes是在任务执行阶段赋予的需要更新的fiber节点上的lane的值
// 当新的更新任务产生时,workInProgressRootRenderLanes不为空,则表示有任务正在执行
// 那么则直接返回这个正在执行的任务的lane,那么当前新的任务则会和现有的任务进行一次批量更新
return pickArbitraryLane(workInProgressRootRenderLanes);
}
// 检查当前事件是否是过渡优先级
// 如果是的话,则返回一个过渡优先级
// 过渡优先级的分配规则:
// 产生的任务A给它分配为TransitionLanes的第一位:TransitionLane1 = 0b0000000000000000000000001000000
// 现在又产生了任务B,那么则从A的位置向左移动一位: TransitionLane2 = 0b0000000000000000000000010000000
// 后续产生的任务则会一次向后移动,直到移动到最后一位
// 过渡优先级共有16位:TransitionLanes = 0b0000000001111111111111111000000
// 当所有位都使用完后,则又从第一位开始赋予事件过渡优先级
const isTransition = requestCurrentTransition() !== NoTransition;
if (isTransition) {
if (currentEventTransitionLane === NoLane) {
currentEventTransitionLane = claimNextTransitionLane();
}
return currentEventTransitionLane;
}
// 在react的内部事件中触发的更新事件,比如:onClick等,会在触发事件的时候为当前事件设置一个优先级,可以直接拿来使用
const updateLane: Lane = (getCurrentUpdatePriority(): any);
if (updateLane !== NoLane) {
return updateLane;
}
// 在react的外部事件中触发的更新事件,比如:setTimeout等,会在触发事件的时候为当前事件设置一个优先级,可以直接拿来使用
const eventLane: Lane = (getCurrentEventPriority(): any);
return eventLane;
}
创建update对象
将update对象加入到当前fiber对应useState hook的更新队列
// 函数式fiber结构
fiber:{
memoizedState:null, 用来保存hooks链表
updateQueue:{
lastEffect:null,//用来存储effect链表
}
}
// hook数据结构
const hook: Hook =
{
memoizedState: null, // 保持在内存中的局部状态.
baseState: null, // hook.baseQueue中所有update对象合并之后的状态.
baseQueue: null, // 存储update对象的环形链表, 只包括高于本次渲染优先级的update对象.
queue: null, // 存储update对象的环形链表, 包括所有优先级的update对象.
next: null, // next指针, 指向链表中的下一个hook.
}
;
// packages\react-reconciler\src\ReactFiberHooks.new.js
function mountState(initialState) {
# hook加载工作
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
const queue = {
pending: null, // 等待处理的update链表
lanes: NoLanes,
dispatch: null, // dispatchSetState方法
lastRenderedReducer: basicStateReducer, // 一个函数,通过action和lastRenderedState计算最新的state
lastRenderedState: initialState, // 上一次的state
};
hook.queue = queue;
const dispatch = queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue)
# 返回值
return [hook.memoizedState, dispatch];
}
// 加入到更新队列
export function enqueueConcurrentHookUpdate<S, A>(
fiber: Fiber,
queue: HookQueue<S, A>,
update: HookUpdate<S, A>,
lane: Lane,
) {
const interleaved = queue.interleaved;
if (interleaved === null) {
update.next = update;
//将`queue`备份到一个并发队列`concurrentQueues`之中,方便在之后将`queue.interleaved`的内容
//转移到`queue.pending`之上。
pushConcurrentUpdateQueue(queue);
} else {
update.next = interleaved.next;
interleaved.next = update;
}
queue.interleaved = update;
}
queue.interleaved 用来存储update链表,这是一条单向环形链表
开启调度任务
确保调度任务执行