IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    React 复习笔记(下,基于源码) · 看不见我的美 · 是你瞎了眼

    馬腊咯稽发表于 2021-10-10 00:00:00
    love 0
    React 复习笔记(下,基于源码)

    调用 ReactDOM.render 发生了什么?

    1. 确定应用渲染方式;
    2. 创建 fiberRoot 和 rootFiberNode,并将两者用指针指向彼此;
    3. 向 rootFiberNode 绑定自身的更新队列;
    4. 清空 container 里的内容;
    5. 向 container 绑定原生事件;
    6. 将 render 的第三个参数的 this 指向应用实例并调用;
    7. 初始化完成后,创建 update,并将 fiber 对应的组件作为 payload;
    8. 将 update 与 fiberNode 相关联,fiberNode 对应的 update 组成循环链表;
    9. 调用 scheduleUpdateOnFiber 开始渲染工作。
     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    
    // ReactDOM 的渲染模式
    export type RootTag = 0 | 1 | 2;
    export const LegacyRoot = 0; // ReactDOM.createLegacyRoot
    export const BlockingRoot = 1; // ReactDOM.createBlockingRoot
    export const ConcurrentRoot = 2; // ReactDOM.createRoot
    // ReactDOM.render
    export function render(
     element: React$Element<any>,
     container: Container,
     callback: ?Function
    ) {
     // 向 DOMContainer 里渲染子树
     return legacyRenderSubtreeIntoContainer(
     null,
     element,
     container,
     false,
     callback
     );
    }
    function legacyRenderSubtreeIntoContainer(
     parentComponent: ?React$Component<any, any>, // null
     children: ReactNodeList, // ReactElement
     container: Container, // DOMContainer
     forceHydrate: boolean, // false
     callback: ?Function // () => {}
    ) {
     let root: RootType = container._reactRootContainer;
     let fiberRoot;
     if (!root) {
     /**
     * 初始化阶段准备工作:
     * 0. legacyCreateRootFromDOMContainer 最终会调用 createRootImpl
     * 1. container._reactRootContainer._internalRoot = createRootImpl(container, 0, undefined)
     * 2. createRootImpl(container, 0, undefined) 返回了 fiberRoot
     * 3. fiberRoot 一个应用只有一个,其 current 属性指向了 rootFiberNode
     * 4. FiberRoot.current 指向了 currentFiber 树
     * 5. FiberRoot.finishedWork 指向了 workInProgress 树
     * 6. 清空 container
     */
     root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
     container,
     forceHydrate
     );
     fiberRoot = root._internalRoot;
     if (typeof callback === 'function') {
     const originalCallback = callback;
     callback = function () {
     /**
     * getPublicRootInstance 会通过 fiberRoot 访问 rootFiberNode
     * 如果 rootFiberNode 没有子节点,返回 null
     * 否则返回 rootFiberNode 子节点的 stateNode
     * 总之就是返回一个非 rootFiberNode 的组建实例
     */
     const instance = getPublicRootInstance(fiberRoot);
     // 调用了 callback 就说明应用初始化完成了
     originalCallback.call(instance);
     };
     }
     // 初始化完成之后,就准备进行应用渲染了
     unbatchedUpdates(() => {
     updateContainer(children, fiberRoot, parentComponent, callback);
     });
     } else {
     // 后续更新需要重新渲染,会走这里
     fiberRoot = root._internalRoot;
     if (typeof callback === 'function') {
     const originalCallback = callback;
     callback = function () {
     const instance = getPublicRootInstance(fiberRoot);
     originalCallback.call(instance);
     };
     }
     updateContainer(children, fiberRoot, parentComponent, callback);
     }
     return getPublicRootInstance(fiberRoot);
    }
    function createRootImpl(
     container: Container,
     tag: RootTag, // 用来表示是否开启“并发模式”
     options: void | RootOptions
    ) {
     const root = createFiberRoot(
     container,
     tag,
     (hydrate = false),
     (hydrationCallbacks = null)
     );
     container.__reactContainer$ = root.current;
     /**
     * 获取应用挂载的 DOM 的 nodeType,nodeType 都有:
     * Node.ELEMENT_NODE 1 元素节点
     * Node.TEXT_NODE 3 文字
     * Node.CDATA_SECTION_NODE 4 <!CDATA[[ … ]]>
     * Node.PROCESSING_INSTRUCTION_NODE 7 <?xml-stylesheet ... ?>
     * Node.COMMENT_NODE 8 注释
     * Node.DOCUMENT_NODE 9 Document
     * Node.DOCUMENT_TYPE_NODE 10 <!DOCTYPE html>
     * Node.DOCUMENT_FRAGMENT_NODE 11 DocumentFragment
     */
     const rootContainerElement =
     container.nodeType === COMMENT_NODE ? container.parentNode : container;
     rootContainerElement._reactListening = true;
     /**
     * allNativeEvents 是包含了离散(触发)事件、阻塞(用户交互)事件、连续(不能被打断)事件、表单事件的 Set
     * nonDelegatedEvents 是放置的是“非委托”事件的 Set (类似于一个白名单)
     * listenToNativeEvent(事件名称,是否在捕获阶段触发,应用根容器,目标元素)
     * 1. 将 selectionchange 绑定到 document
     * 2. 将支持“冒泡”的事件委托到 rootContainerElement
     * 3. 其他的事件除了 scroll 外,绑定到“目标元素”
     * 4. 从 EventTarget.__reactEvents 找到所有绑定的 listener(保存在 Set 里)
     * 5. listener 里如果没有 `${domEventName}__${capture ? 'capture' : 'bubble'}` 会执行 addTrappedEventListener
     * addTrappedEventListener(EventTarget, DOMEventName, EventSystemFlags, isCapture, isDeferred)
     * 1. createEventListenerWrapperWithPriority 会根据事件类型,使用不同的优先级函数包裹 listener(事件优先级)
     * a. dispatchDiscreteEvent 离散事件
     * b. dispatchUserBlockingUpdate 用户阻塞事件
     * c. dispatchEvent 连续事件
     * 2. 对于“touchstart”、“touchmove”、“wheel”三个事件,isPassive=true(提前告知浏览器,我没有阻止默认行为,不要害怕)
     * 3. 根据 isCapture 和 isPassive 将事件绑定分发给了四个函数 addEvent[Bubble/Capture]Listener[WithPassiveFlag]
     * 4. 调用原生 API:addEventListener 和 removeEventListener 进行事件的绑定和移除
     * 总的来说,React 会根据事件 isCapture、isPassive 和优先级,将事件绑定到合适的 target 上
     */
     allNativeEvents.forEach(domEventName => {
     if (!nonDelegatedEvents.has(domEventName)) {
     // 可以使用“事件委托”
     listenToNativeEvent(
     domEventName,
     false, // “事件委托”需要“事件冒泡”的支持
     rootContainerElement,
     null
     );
     }
     listenToNativeEvent(domEventName, true, rootContainerElement, null);
     });
     return root;
    }
    // 创建 fiberRoot 和 rootFiberNode,并将两者关联起来
    export function createFiberRoot(
     containerInfo: any,
     tag: RootTag,
     hydrate: boolean
    ): FiberRoot {
     const root: FiberRoot = {
     tag,
     containerInfo, // DOMContainer
     pendingChildren: null,
     current: null,
     pingCache: null,
     finishedWork: null,
     timeoutHandle: noTimeout, // -1
     context: null,
     pendingContext: null,
     hydrate,
     callbackNode: null,
     callbackPriority: NoLanePriority, // 0
     eventTimes: createLaneMap(NoLanes), // []
     expirationTimes: createLaneMap(NoTimestamp),
     pendingLanes: NoLanes,
     suspendedLanes: NoLanes,
     pingedLanes: NoLanes,
     expiredLanes: NoLanes,
     mutableReadLanes: NoLanes,
     finishedLanes: NoLanes,
     entangledLanes: NoLanes,
     entanglements: createLaneMap(NoLanes)
     };
     const uninitializedFiber: FiberNode = {
     tag: 3,
     key: null,
     elementType: null,
     type: null,
     stateNode: null,
     return: null,
     child: null,
     sibling: null,
     index: 0,
     ref: null,
     pendingProps: null,
     memoizedProps: null,
     updateQueue: null,
     memoizedState: null,
     dependencies: null,
     /**
     * mode 有六种模式:
     * 0b00000 NoMode 普通模式
     * 0b00001 StrictMode 严格模式
     * 0b00010 BlockingMode 块模式
     * 0b00100 ConcurrentMode 并发模式
     * 0b01000 ProfileMode 性能测试模式
     * 0b10000 DebugTracingMode Debug 模式
     */
     mode: NoMode | ConcurrentMode | BlockingMode | StrictMode,
     flags: NoFlags,
     nextEffect: null,
     firstEffect: null,
     lastEffect: null,
     lanes: NoLanes,
     childLanes: NoLanes,
     alternate: null
     };
     // fiberRoot 的 current 指向 rootFiberNode
     root.current = uninitializedFiber;
     // rootFiberNode 的 stateNode 指向 fiberRoot
     uninitializedFiber.stateNode = root;
     // rootFiberNode 的更新队列(链表)
     uninitializedFiber.updateQueue = {
     baseState: uninitializedFiber.memoizedState,
     firstBaseUpdate: null,
     lastBaseUpdate: null,
     shared: {
     pending: null
     },
     effects: null
     };
     return root;
    }
    // 创建更新,安排渲染
    export function updateContainer(
     element: ReactNodeList,
     container: OpaqueRoot,
     parentComponent: ?React$Component<any, any>,
     callback: ?Function
    ): Lane {
     const current = container.current; // rootFiberNode
     const eventTime = requestEventTime(); // 当前时间
     const lane = requestUpdateLane(current); // 优先级
     const context = getContextForSubtree(parentComponent); // emptyContextObject = {}
     if (container.context === null) {
     container.context = context; // 走这儿
     } else {
     container.pendingContext = context;
     }
     // 创建了一个 update 对象
     const update = {
     callback: null,
     eventTime,
     lane,
     next: null,
     payload: { element },
     tag: UpdateState // 0
     };
     callback = callback === undefined ? null : callback;
     if (callback !== null) {
     update.callback = callback;
     }
     /**
     * enqueueUpdate 做了一下事情:将 fiber 节点和 update 关联起来
     * const updateQueue = fiber.updateQueue;
     * const sharedQueue = fiber.updateQueue.shared;
     * const pending = sharedQueue.pending;
     * update.next = update; // 每个 fiberNode 的所有 update 组成了一个链表,只不过现在链表里只有一个元素
     * sharedQueue.pending = update;
     */
     enqueueUpdate(current, update);
     // 执行渲染工作
     scheduleUpdateOnFiber(current, lane, eventTime);
     return lane;
    }
    /**
     * 从 17 开始,e.persist() 将不再生效,因为 SyntheticEvent 不再放入事件池中
     * React 将不再向 document 附加事件处理器
     * 而会将事件处理器附加到渲染 React 树的根 DOM 容器中
     * 原因:如果页面上有多个 React 版本,会破坏 e.stopPropagation()
     */
    export function listenToNativeEvent(
     domEventName: DOMEventName,
     isCapturePhaseListener: boolean,
     rootContainerElement: EventTarget,
     targetElement: Element | null,
     eventSystemFlags?: EventSystemFlags = 0
    ): void {
     let target = rootContainerElement;
     if (
     domEventName === 'selectionchange' &&
     rootContainerElement.nodeType !== DOCUMENT_NODE
     ) {
     /**
     * selectionchange 事件需要直接绑定在 document 上
     * ownerDocument 返回节点的顶层的 document 对象
     */
     target = rootContainerElement.ownerDocument;
     }
     /**
     * 如果事件可以被委托(或者在捕获阶段触发)
     * 那就注册在 rootContainerElement 上
     * 否则,就绑定在 targetElement 上
     */
     if (
     targetElement !== null &&
     !isCapturePhaseListener &&
     nonDelegatedEvents.has(domEventName)
     ) {
     /**
     * 所有不能代理的事件(多媒体事件+cancel+close+invalid+load+scroll+toggle)
     * 都绑定在 targetElement 上
     */
     if (domEventName !== 'scroll') {
     // 除了 scroll 事件,因为任何元素都可以滚动
     return;
     }
     eventSystemFlags |= IS_NON_DELEGATED;
     target = targetElement;
     }
     // target['__reactEvents$' + Math.random().toString(36).slice(2)] = new Set()
     const listenerSet = getEventListenerSet(target);
     // `${domEventName}__${isCapturePhaseListener ? 'capture' : 'bubble'}`
     const listenerSetKey = getListenerSetKey(
     domEventName,
     isCapturePhaseListener
     );
     if (!listenerSet.has(listenerSetKey)) {
     if (isCapturePhaseListener) {
     eventSystemFlags |= IS_CAPTURE_PHASE;
     }
     addTrappedEventListener(
     target,
     domEventName,
     eventSystemFlags,
     isCapturePhaseListener
     );
     listenerSet.add(listenerSetKey);
     }
    }
    

    调用 scheduleUpdateOnFiber 发生了什么?

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    
    export function scheduleUpdateOnFiber(
     fiber: Fiber,
     lane: Lane,
     eventTime: number
    ) {
     // 检查是否有嵌套更新,如果嵌套超过 50 层,终止调度
     checkForNestedUpdates();
     // 标记从 fiberNode 到 fiberRoot 的更新时间
     const root = markUpdateLaneFromFiberToRoot(fiber, lane);
     if (root === null) {
     return null;
     }
     // 标记 fiberRoot 有挂起的更新
     markRootUpdated(root, lane, eventTime);
     if (root === workInProgressRoot) {
     /**
     * 接收到在渲染过程中的树的更新,标记 fiberRoot 上存在交叉更新工作
     * 除非 deferRenderPhaseUpdateToNextBatch 标志关闭并且这是 render 阶段更新
     * 在这种情况下,出于向后兼容的原因,我们不会将 render 阶段更新视为交错处理
     */
     workInProgressRootUpdatedLanes = workInProgressRootUpdatedLanes | lane;
     }
     /**
     * 获取 currentPriorityLevel 的值和 Scheduler_*Priority 做对比;获取当前优先级
     * ImmediatePriority ||
     * UserBlockingPriority||
     * NormalPriority ||
     * LowPriority ||
     * IdlePriority
     */
     const priorityLevel = getCurrentPriorityLevel();
     if (lane === SyncLane) {
     // 初次渲染是同步的
     if (
     // 检查是否在 unbatchedUpdates 内部
     (executionContext & LegacyUnbatchedContext) !== NoContext &&
     // 检查是否还没有渲染
     (executionContext & (RenderContext | CommitContext)) === NoContext
     ) {
     // 在 fiberRoot 上注册挂起的交互,以避免丢失跟踪的交互数据
     schedulePendingInteractions(root, lane);
     /**
     * 一种边缘情况:在 batchedUpdates 内部,
     * ReactDOM.render 渲染的 root 节点挂载应该是同步的,
     * 而布局更新应该推迟到批处理结束
     */
     performSyncWorkOnRoot(root);
     } else {
     ensureRootIsScheduled(root, eventTime);
     schedulePendingInteractions(root, lane);
     if (executionContext === NoContext) {
     /**
     * 现在就开始同步工作,除非我们已经开始或在批处理中
     * 这有意在 scheduleUpdateOnFiber 中(而不是 scheduleCallbackForFiber 中)
     * 以保留 schedule 回调而不立即执行它的能力
     * 我们只对用户发起的更新执行此操作,以保留遗留模式的历史行为
     */
     resetRenderTimer();
     flushSyncCallbackQueue();
     }
     }
     } else {
     // 计划离散更新,但仅在它不是同步的情况下
     if (
     (executionContext & DiscreteEventContext) !== NoContext &&
     // 只有用户阻塞或更高优先级的更新才被视为离散的
     (priorityLevel === UserBlockingSchedulerPriority ||
     priorityLevel === ImmediateSchedulerPriority)
     ) {
     // 跟踪每个 root 的最低优先级的离散更新,以便在必要时及早运行他们
     if (rootsWithPendingDiscreteUpdates === null) {
     rootsWithPendingDiscreteUpdates = new Set([root]);
     } else {
     rootsWithPendingDiscreteUpdates.add(root);
     }
     }
     // 如果回调是同步的,则在之后安排其他更新
     ensureRootIsScheduled(root, eventTime);
     schedulePendingInteractions(root, lane);
     }
     /**
     * 我们假设它和被更新的 root 相同,因为单个 root 的应用很常见
     * 如果不是同一个 root,那也不是啥难事
     * 我们只是倾向于把更多的东西 batch 在一起
     */
     mostRecentlyUpdatedRoot = root;
    }
    

    scheduler(任务调度,高优先级的任务先进行 render)

    对任务按照优先级进行调度(高优先级的任务打断低优先级的任务的执行),所有任务保存在最小堆中(方便取出优先级最高的任务)。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    
    /**
     * 根据 priorityLevel 新建 task,task 的内容为 callback
     * task 根据优先级的不同保存在不同的队列里(taskQueue、timerQueue)
     * taskQueue 里存放着需要执行的任务
     * timerQueue 里存放着等待执行的任务
     */
    function unstable_scheduleCallback(priorityLevel, callback, options) {
     var currentTime = getCurrentTime();
     var startTime;
     // 根据 option 计算任务最早开始时间(任务不能早于这个时间运行)
     if (typeof options === 'object' && options !== null) {
     var delay = options.delay;
     if (typeof delay === 'number' && delay > 0) {
     startTime = currentTime + delay;
     } else {
     startTime = currentTime;
     }
     } else {
     startTime = currentTime;
     }
     // 根据优先级计算任务最多可以延后多久
     var timeout;
     switch (priorityLevel) {
     case ImmediatePriority:
     timeout = IMMEDIATE_PRIORITY_TIMEOUT; // -1
     break;
     case UserBlockingPriority:
     timeout = USER_BLOCKING_PRIORITY_TIMEOUT; // 250
     break;
     case IdlePriority:
     // V8 在 32 位系统上能展示的最大整数 Math.pow(2, 30) - 1
     timeout = IDLE_PRIORITY_TIMEOUT;
     break;
     case LowPriority:
     timeout = LOW_PRIORITY_TIMEOUT; // 10000
     break;
     case NormalPriority:
     default: // 5000
     timeout = NORMAL_PRIORITY_TIMEOUT;
     break;
     }
     // 任务过期时间(晚于这个时间必须立即执行)
     var expirationTime = startTime + timeout;
     // 新建任务
     var newTask = {
     id: taskIdCounter++, // 任务 ID
     callback, // 任务内容
     priorityLevel, // 优先级
     startTime, // 最早执行时间
     expirationTime, // 最晚执行时间(过期时间)
     sortIndex: -1 // 值越小,优先级越高
     };
     if (startTime > currentTime) {
     // 任务不需要现在就执行
     newTask.sortIndex = startTime;
     // 不需要立即执行的任务保存在 timerQueue
     push(timerQueue, newTask);
     if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
     // taskQueue 队列里的任务执行完了,并且新建的任务排在 timerQueue 的第一个,说明此时此刻没有任务需要执行
     if (isHostTimeoutScheduled) {
     cancelHostTimeout();
     } else {
     isHostTimeoutScheduled = true;
     }
     // 安排一个 timeout
     requestHostTimeout(handleTimeout, startTime - currentTime);
     }
     } else {
     newTask.sortIndex = expirationTime;
     // 将要处理的任务放在 taskQueue
     push(taskQueue, newTask);
     // 接着干活,如果没时间了,等到一次 yield
     if (!isHostCallbackScheduled && !isPerformingWork) {
     isHostCallbackScheduled = true;
     requestHostCallback(flushWork);
     }
     }
     return newTask;
    }
    

    reconciler(render 阶段,构建 workInProgress,收集要更新的节点)

    通过组件返回的 React 元素和 current 树构建 workInProgress 树,找出前后变化并打上标记,将所有变化收集到 effectList 链表(链表对异步友好的;链表在循环时不用每次都进入递归函数,重新生成执行上下文,变量对象,激活对象,性能比递归好)里。

    • current 与 workInProgress:

      • 在 React 应用中最多会同时存在两棵 fiber 树,current 树对应当前渲染完成的视图;当状态更新后会在内存中构建 workInProgress 树;
      • 当 fiberRoot 的 current 指向 workInProgress 时,workInProgress 树也就成为了 current 树;
      • 每次状态更新都会在内存中构建 workInProgress 树,应用通过 current 与 workInProgress 的替换完成更新;
      • current 树的节点与 workInProgress 树的节点通过自身 alternate 指向对方。
    • 应用 mount(首次渲染)时:

      • 首次执行 ReactDOM.render 会创建 fiberRoot(应用根节点)和 rootFiberNode(组件树根节点);
      • fiberRoot 有两个指针,current 指向 current 树,finishedWork 指向 workInProgress 树;
      • 多次调用 ReactDOM.render 渲染不同的组件树,他们会拥有不同的 rootFiber;但是 fiberRoot 只有一个;
      • 首次渲染时页面没有内容,current 树为空;接下来会在内存中创建 fiberNode,构成 workInProgress;
      • workInProgress 会在 commit 阶段同步更新到视图中。
    • 应用 update(状态变化)时:

      • 应用重新进入 render 阶段,调用组件 render 方法,重新构建 workInProgress;
      • 构建 workInProgress 的过程中会和 current 进行比较,并在需要改变的节点打上标记;
      • 根据收集到的标记,更新视图。

    renderer(commit 阶段,更新页面)

    遍历 effectList,更新视图(ReactArt、ReactDOM、ReactNative);这个阶段是同步的,不能被打断:

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    
    /**
     * 此时,有两棵树和一个链表:
     * current 表示当前屏幕显示内容
     * workInProgress 表示即将更新到屏幕上的内容
     * effectList 表示本次更新要执行的副作用(DOM 更新、生命周期调用、setState 回调、hooks 调用...)
     */
    function commitRoot(root) {
     const renderPriorityLevel = getCurrentPriorityLevel();
     runWithPriority(
     ImmediateSchedulerPriority,
     commitRootImpl.bind(null, root, renderPriorityLevel)
     );
     return null;
    }
    function commitRootImpl(root, renderPriorityLevel) {
     do {
     /**
     * 触发 useEffect 回调与其他同步任务,
     * 由于这些任务可能触发新的渲染,
     * 所以,这里要一直遍历执行直到没有任务
     */
     flushPassiveEffects();
     } while (rootWithPendingPassiveEffects !== null);
     /**
     * root 指 fiberRoot
     * root.finishedWork 指当前应用的 rootFiber
     */
     const finishedWork = root.finishedWork;
     const lanes = root.finishedLanes;
     if (finishedWork === null) {
     return null;
     }
     root.finishedWork = null;
     root.finishedLanes = NoLanes;
     root.callbackNode = null; // 重置 scheduler 绑定的回调函数
     let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
     // 清除已完成的离散型 updates,例如:用户鼠标点击触发的更新
     if (rootsWithPendingDiscreteUpdates !== null) {
     if (
     !hasDiscreteLanes(remainingLanes) &&
     rootsWithPendingDiscreteUpdates.has(root)
     ) {
     rootsWithPendingDiscreteUpdates.delete(root);
     }
     }
     // 重置全局变量
     if (root === workInProgressRoot) {
     workInProgressRoot = null;
     workInProgress = null;
     workInProgressRootRenderLanes = NoLanes;
     } else {
     // ...
     }
     /**
     * 将 effectList 赋值给 firstEffect
     * 由于每个 fiber 的 effectList 只包含他的子孙节点
     * 所以根节点如果有 effectTag 则不会被包含进来
     * 所以这里将有 effectTag 的根节点插入到 effectList 尾部
     * 这样才能保证有 effect 的 fiber 都在 effectList 中
     */
     let firstEffect;
     if (finishedWork.flags > PerformedWork) {
     /**
     * 一个 fiber 的 effectList 只包括它的子节点,而不是其本身
     * 因此,如果 root 有一个 effect,我们需要把它加到列表的最后
     */
     if (finishedWork.lastEffect !== null) {
     finishedWork.lastEffect.nextEffect = finishedWork;
     firstEffect = finishedWork.firstEffect;
     } else {
     firstEffect = finishedWork;
     }
     } else {
     // 根节点没有 effectTag
     firstEffect = finishedWork.firstEffect;
     }
     if (firstEffect !== null) {
     let previousLanePriority;
     if (decoupleUpdatePriorityFromScheduler) {
     previousLanePriority = getCurrentUpdateLanePriority();
     setCurrentUpdateLanePriority(SyncLanePriority);
     }
     const prevExecutionContext = executionContext;
     executionContext |= CommitContext;
     const prevInteractions = pushInteractions(root);
     // 在调用生命周期之前置为 null
     ReactCurrentOwner.current = null;
     /**
     * commit 阶段被分成了几个阶段,每个阶段都会遍历一遍 effectList
     * 所有的 MutationEffects 都会在所有的 LayoutEffects 前执行
     * 第一个阶段『before mutation』:
     * 在修改之前,读取主树的 state
     * 调用 getSnapshotBeforeUpdate
     * 调度 useEffect
     */
     focusedInstanceHandle = prepareForCommit(root.containerInfo);
     shouldFireAfterActiveInstanceBlur = false;
     nextEffect = firstEffect; // 从 firstEffect 开始遍历 effectList(第一遍)
     do {
     try {
     commitBeforeMutationEffects();
     } catch (error) {
     captureCommitPhaseError(nextEffect, error);
     nextEffect = nextEffect.nextEffect;
     }
     } while (nextEffect !== null);
     // 不再需要追踪激活的 fiber 实例
     focusedInstanceHandle = null;
     /**
     * 第二个阶段『mutation』:
     * 更新 DOM
     * 调用 componentWillUnmount
     * 调用 useLayoutEffect 销毁回调
     */
     nextEffect = firstEffect; // 从 firstEffect 开始遍历 effectList(第二遍)
     do {
     try {
     commitMutationEffects(root, renderPriorityLevel);
     } catch (error) {
     captureCommitPhaseError(nextEffect, error);
     nextEffect = nextEffect.nextEffect;
     }
     } while (nextEffect !== null);
     if (shouldFireAfterActiveInstanceBlur) {
     afterActiveInstanceBlur();
     }
     resetAfterCommit(root.containerInfo);
     /**
     * commit 之后,WIP 树就成为了 current 树
     * 这是为了在调用 componentWillUnmount 期间,还可以获取卸载前组件的信息
     * 也是为了在下一个阶段之前,在 componentDidMount/Update 调用期间,WIP 是 current 树
     */
     root.current = finishedWork; // fiberRoot 的 current 指针指向了 finishedWork 所指向的 WIP
     /**
     * 第三个阶段『layout』:
     * 调用主树修改之后需要执行的副作用 setState 第二个参数
     * 类组件的生命周期也会在这里调用 componentDidMount/Update
     */
     nextEffect = firstEffect; // 从 firstEffect 开始遍历 effectList(第三遍)
     do {
     try {
     commitLayoutEffects(root, lanes);
     } catch (error) {
     captureCommitPhaseError(nextEffect, error);
     nextEffect = nextEffect.nextEffect;
     }
     } while (nextEffect !== null);
     nextEffect = null;
     /**
     * 告诉 Scheduler 在当前帧停一下,让浏览器渲染一下页面
     * yieldInterval = 5,每帧只给 5ms 让 React 执行 render 阶段的工作
     */
     requestPaint(); // needsPaint=true
     if (enableSchedulerTracing) {
     popInteractions(prevInteractions);
     }
     executionContext = prevExecutionContext;
     if (decoupleUpdatePriorityFromScheduler && previousLanePriority != null) {
     setCurrentUpdateLanePriority(previousLanePriority);
     }
     } else {
     // effectList 中没有 effect,直接将 WIP 树切换为 current 树
     root.current = finishedWork;
     }
     const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
     // useEffect 相关
     if (rootDoesHavePassiveEffects) {
     // 当前 commit 有副作用,在完成 layout 之前,不要调用副作用回调函数
     rootDoesHavePassiveEffects = false;
     rootWithPendingPassiveEffects = root;
     pendingPassiveEffectsLanes = lanes;
     pendingPassiveEffectsRenderPriority = renderPriorityLevel;
     } else {
     /**
     * 处理完 effect 了,清除 nextEffect
     * 如果有副作用,会在 flushPassiveEffects 里消耗掉
     */
     nextEffect = firstEffect;
     while (nextEffect !== null) {
     const nextNextEffect = nextEffect.nextEffect;
     nextEffect.nextEffect = null;
     if (nextEffect.flags & Deletion) {
     detachFiberAfterEffects(nextEffect);
     }
     nextEffect = nextNextEffect;
     }
     }
     remainingLanes = root.pendingLanes;
     // 检查 root 上有没有漏掉的 work
     if (remainingLanes !== NoLanes) {
     if (enableSchedulerTracing) {
     if (spawnedWorkDuringRender !== null) {
     const expirationTimes = spawnedWorkDuringRender;
     spawnedWorkDuringRender = null;
     for (let i = 0; i < expirationTimes.length; i++) {
     scheduleInteractions(
     root,
     expirationTimes[i],
     root.memoizedInteractions
     );
     }
     }
     schedulePendingInteractions(root, remainingLanes);
     }
     } else {
     // 没任务的话,清除失败的错误边界???
     legacyErrorBoundariesThatAlreadyFailed = null;
     }
     // 检测无限循环的同步任务,判断是否进入了死循环
     if (remainingLanes === SyncLane) {
     if (root === rootWithNestedUpdates) {
     nestedUpdateCount++;
     } else {
     nestedUpdateCount = 0;
     rootWithNestedUpdates = root;
     }
     } else {
     nestedUpdateCount = 0;
     }
     // 在离开 commitRoot 函数前调用,触发一次新的调度,确保任何附加的任务被调度
     ensureRootIsScheduled(root, now());
     // 处理未捕获的错误
     if (hasUncaughtError) {
     hasUncaughtError = false;
     const error = firstUncaughtError;
     firstUncaughtError = null;
     throw error;
     }
     if ((executionContext & LegacyUnbatchedContext) !== NoContext) {
     return null;
     }
     /**
     * 执行同步任务,这样同步任务不需要等到下次事件循环再执行
     * 比如,在 componentDidMount 中执行 setState 创建的更新会在这里被同步执行
     */
     flushSyncCallbackQueue();
     return null;
    }
    
    • beforeMutation(渲染前):遍历 effectList 并调用 commitBeforeMutationEffects;调用 getSnapshotBeforeUpdate;调度 useEffect:
     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    
    /**
     * 1. 调用 getSnapshotBeforeUpdate
     * 2. 调用 useEffect 的销毁函数
     * 3. 异步调度 useEffect
     */
    function commitBeforeMutationEffects() {
     while (nextEffect !== null) {
     const current = nextEffect.alternate;
     if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
     // 处理 DOM 节点渲染/删除后的 autoFocus、blur 逻辑
     }
     const flags = nextEffect.flags;
     if ((flags & Snapshot) !== NoFlags) {
     // commitBeforeMutationEffectOnFiber 会调用 getSnapshotBeforeUpdate
     // snapshot = instance.getSnapshotBeforeUpdate(
     // finishedWork.elementType === finishedWork.type
     // ? prevProps
     // : resolveDefaultProps(finishedWork.type, prevProps),
     // prevState
     // );
     // 将返回值存储在内部属性上,方便 componentDidUpdate 获取
     // instance.__reactInternalSnapshotBeforeUpdate = snapshot;
     commitBeforeMutationEffectOnFiber(current, nextEffect);
     }
     // 异步调度 useEffect
     if ((flags & Passive) !== NoFlags) {
     if (!rootDoesHavePassiveEffects) {
     rootDoesHavePassiveEffects = true;
     scheduleCallback(NormalSchedulerPriority, () => {
     // 从全局变量 rootWithPendingPassiveEffects 获取 effectList,触发 effect 回调函数
     flushPassiveEffects();
     return null;
     });
     }
     }
     nextEffect = nextEffect.nextEffect;
     }
    }
    function flushPassiveEffectsImpl() {
     if (rootWithPendingPassiveEffects === null) {
     return false;
     }
     const root = rootWithPendingPassiveEffects;
     const lanes = pendingPassiveEffectsLanes;
     rootWithPendingPassiveEffects = null;
     pendingPassiveEffectsLanes = NoLanes;
     const prevExecutionContext = executionContext;
     executionContext |= CommitContext;
     const prevInteractions = pushInteractions(root);
     /**
     * 任何副作用函数被运行之前,应该先运行清理函数
     * 否则兄弟组件的副作用可能会相互影响
     * 例如,一个组件的销毁函数可能无意间覆盖(另一个组件的副作用设置的)ref 的值
     */
     const unmountEffects = pendingPassiveHookEffectsUnmount;
     pendingPassiveHookEffectsUnmount = [];
     for (let i = 0; i < unmountEffects.length; i += 2) {
     const effect = unmountEffects[i];
     const fiber = unmountEffects[i + 1];
     const destroy = effect.destroy;
     effect.destroy = undefined;
     if (typeof destroy === 'function') {
     try {
     destroy();
     } catch (error) {
     captureCommitPhaseError(fiber, error);
     }
     }
     }
     const mountEffects = pendingPassiveHookEffectsMount;
     pendingPassiveHookEffectsMount = [];
     for (let i = 0; i < mountEffects.length; i += 2) {
     const effect = mountEffects[i];
     const fiber = mountEffects[i + 1];
     try {
     const create = effect.create;
     effect.destroy = create();
     } catch (error) {
     captureCommitPhaseError(fiber, error);
     }
     }
     let effect = root.current.firstEffect;
     while (effect !== null) {
     const nextNextEffect = effect.nextEffect;
     // 移出指针,方便垃圾回收
     effect.nextEffect = null;
     if (effect.flags & Deletion) {
     detachFiberAfterEffects(effect);
     }
     effect = nextNextEffect;
     }
     if (enableSchedulerTracing) {
     popInteractions(prevInteractions);
     finishPendingInteractions(root, lanes);
     }
     executionContext = prevExecutionContext;
     flushSyncCallbackQueue();
     nestedPassiveUpdateCount =
     rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;
     return true;
    }
    
    • mutation(渲染中):使用 Host API 对视图进行更新;
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    
    /**
     * 1. 调用 commitDetachRef 解绑 ref
     * 2. 根据 effectTag 处理 DOM
     * 3. 执行 useLayoutEffect 的销毁函数(commitWork 会调用 commitHookEffectListUnmount)
     */
    function commitMutationEffects(root, renderPriorityLevel) {
     // 遍历 effectList
     while (nextEffect !== null) {
     const flags = nextEffect.flags;
     if (flags & ContentReset) {
     commitResetTextContent(nextEffect);
     }
     // 更新 ref
     if (flags & Ref) {
     const current = nextEffect.alternate;
     if (current !== null) {
     commitDetachRef(current);
     }
     }
     // 根据 effectTag 处理 DOM
     const primaryFlags = flags & (Placement | Update | Deletion | Hydrating);
     switch (primaryFlags) {
     // 插入 DOM
     case Placement: {
     /**
     * 获取父级 DOM 节点
     * 获取 fiber 的 DOM 兄弟节点(fiber 树和 DOM 树并不是一一对应的)
     * 调用 parentNode.insertBefore 或 parentNode.appendChild 执行 DOM 插入操作
     */
     commitPlacement(nextEffect);
     nextEffect.flags &= ~Placement;
     break;
     }
     // 插入并更新 DOM
     case PlacementAndUpdate: {
     commitPlacement(nextEffect);
     nextEffect.flags &= ~Placement;
     const current = nextEffect.alternate;
     commitWork(current, nextEffect);
     break;
     }
     // 更新 DOM
     case Update: {
     /**
     * fiber.tag === FunctionComponent,调用 commitHookEffectListUnmount
     * 遍历 effectList,执行所有 useLayoutEffect 的销毁函数
     * fiber.tag === HostComponent,调用 commitUpdate
     * 在 updateDOMProperties 中将 render 阶段 completeWork 中为 fiber 赋值的 updateQueue 对应的内容渲染在页面上
     */
     const current = nextEffect.alternate;
     commitWork(current, nextEffect);
     break;
     }
     // 删除 DOM
     case Deletion: {
     /**
     * 递归调用 fiber 及其子孙 fiber 中 tag === ClassComponent 的 componentWillUnmount 生命周期钩子,从页面移除 fiber 对应的 DOM
     * 解绑 ref
     * 调度 useLayoutEffect 的销毁函数
     */
     commitDeletion(root, nextEffect, renderPriorityLevel);
     break;
     }
     // ...
     }
     nextEffect = nextEffect.nextEffect;
     }
    }
    
    • layout(渲染后):同步调用 componentDidMount/Update。
     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    
    /**
     * 1. 调用 commitLayoutEffectOnFiber(commitLifeCycles) 执行相应的生命周期函数
     * 2. 调用 commitAttachRef 更新 ref
     * 3. 调用 setState 第二个参数
     */
    function commitLayoutEffects(root, committedLanes) {
     while (nextEffect !== null) {
     const flags = nextEffect.flags;
     // 调用生命周期钩子和 hook
     if (flags & (Update | Callback)) {
     const current = nextEffect.alternate;
     /**
     * commitLifeCycles 的同名函数
     * current === null ? componentDidMount : componentDidUpdate
     * 调用 setState 的第二个参数(回调函数)
     * 调用 useLayoutEffect 的回调函数(上一次的销毁函数是在 mutation 阶段调用)
     * 调度 useEffect 的回调和销毁函数
     * ReactDOM.render 的第三个参数(回调函数)
     */
     commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
     }
     // 赋值 ref
     if (flags & Ref) {
     /**
     * 如果 ref 是函数形式,调用回调函数
     * 如果 ref 是实例形式,赋值 ref.current
     */
     commitAttachRef(nextEffect);
     }
     nextEffect = nextEffect.nextEffect;
     }
    }
    function commitLifeCycles(
     finishedRoot: FiberRoot,
     current: Fiber | null,
     finishedWork: Fiber,
     committedLanes: Lanes
    ): void {
     switch (finishedWork.tag) {
     case FunctionComponent:
     case ForwardRef:
     case SimpleMemoComponent:
     case Block: {
     /**
     * 在 mutation 阶段,LayoutEffects 已经销毁
     * 酱紫是为了防止兄弟组件的 Effects 相互干扰
     * 调用 useLayoutEffect
     */
     commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
     // 调度 useEffect
     schedulePassiveEffects(finishedWork);
     return;
     }
     case ClassComponent: {
     const instance = finishedWork.stateNode;
     if (finishedWork.flags & Update) {
     if (current === null) {
     // 执行 componentDidMount
     instance.componentDidMount();
     } else {
     const prevProps =
     finishedWork.elementType === finishedWork.type
     ? current.memoizedProps
     : resolveDefaultProps(finishedWork.type, current.memoizedProps);
     const prevState = current.memoizedState;
     // 执行 componentDidUpdate
     instance.componentDidUpdate(
     prevProps,
     prevState,
     instance.__reactInternalSnapshotBeforeUpdate
     );
     }
     }
     // 执行 setState 第二个参数
     const updateQueue: UpdateQueue | null = finishedWork.updateQueue;
     if (updateQueue !== null) {
     commitUpdateQueue(finishedWork, updateQueue, instance);
     }
     return;
     }
     case HostRoot: {
     const updateQueue: UpdateQueue | null = finishedWork.updateQueue;
     if (updateQueue !== null) {
     let instance = null;
     if (finishedWork.child !== null) {
     switch (finishedWork.child.tag) {
     case HostComponent:
     instance = getPublicInstance(finishedWork.child.stateNode);
     break;
     case ClassComponent:
     instance = finishedWork.child.stateNode;
     break;
     }
     }
     commitUpdateQueue(finishedWork, updateQueue, instance);
     }
     return;
     }
     case HostComponent: {
     const instance: Instance = finishedWork.stateNode;
     if (current === null && finishedWork.flags & Update) {
     const type = finishedWork.type;
     const props = finishedWork.memoizedProps;
     commitMount(instance, type, props, finishedWork);
     }
     return;
     }
     case SuspenseComponent: {
     commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
     return;
     }
     case HostText:
     case HostPortal:
     case SuspenseListComponent:
     case IncompleteClassComponent:
     case FundamentalComponent:
     case ScopeComponent:
     case OffscreenComponent:
     case LegacyHiddenComponent:
     return;
     }
    }
    

    diff

    diff 发生在 update 阶段;通过 current 树和组件返回的 JSX 对象,生成 workInProgress 树;并收集需要更新的 fiber 节点,以寻求更新视图的最优方案;它有以下准则:

    • 只对同级元素进行 diff;如果一个节点在前后两次更新中跨越了层级,那么不会复用节点;
    • 两个不同类型的元素会产生出不同的树;卸载旧的(及其子树),创建新的(及其子树);
    • 通过设置 key 属性,来告知渲染哪些子元素在不同的渲染下可以保持不变;
    • 同类型的组件元素,组建实例保持不变;
    • 同类型的元素,保留节点,更新属性。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    
    // 给子节点打上 effectTag 并添加到 sideEffectList
    function reconcileChildFibers(
     returnFiber,
     currentFirstChild,
     newChild, // JSX 对象
     lanes
    ) {
     const isUnkeyedTopLevelFragment =
     typeof newChild === 'object' &&
     newChild !== null &&
     newChild.type === REACT_FRAGMENT_TYPE &&
     newChild.key === null;
     if (isUnkeyedTopLevelFragment) {
     // 跳过 React.Fragment
     newChild = newChild.props.children;
     }
     const isObject = typeof newChild === 'object' && newChild !== null;
     // newChild 类型为 object,同级仅有一个节点
     if (isObject) {
     switch (newChild.$$typeof) {
     case REACT_ELEMENT_TYPE: //...
     case REACT_PORTAL_TYPE: //...
     case REACT_LAZY_TYPE: //...
     }
     }
     // newChild 类型为 string、number,同级仅有一个节点
     if (typeof newChild === 'string' || typeof newChild === 'number') {
     return placeSingleChild(
     reconcileSingleTextNode(
     returnFiber,
     currentFirstChild,
     '' + newChild,
     lanes
     )
     );
     }
     // newChild 类型为 array,同级有多个节点
     if (isArray(newChild)) {
     return reconcileChildrenArray(
     returnFiber,
     currentFirstChild,
     newChild,
     lanes
     );
     }
     // newChild 类型为 iterator
     if (getIteratorFn(newChild)) {
     return reconcileChildrenIterator(
     returnFiber,
     currentFirstChild,
     newChild,
     lanes
     );
     }
     if (isObject) {
     throwOnInvalidObjectType(returnFiber, newChild);
     }
     if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
     // ...
     }
     // 删除节点
     return deleteRemainingChildren(returnFiber, currentFirstChild);
    }
    
    • 单节点 diff

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
    function reconcileSingleElement(
     returnFiber,
     currentFirstChild,
     element,
     lanes
    ) {
     const key = element.key;
     let child = currentFirstChild;
     // 判断是否存在对应的 DOM
     while (child !== null) {
     // 判断 key 是否相同
     if (child.key === key) {
     switch (child.tag) {
     case Fragment: // ...
     case Block: // ...
     default: {
     // type 相同表示可以复用
     if (child.elementType === element.type) {
     deleteRemainingChildren(returnFiber, child.sibling);
     const existing = useFiber(child, element.props);
     existing.ref = coerceRef(returnFiber, child, element);
     existing.return = returnFiber;
     return existing;
     }
     break;
     }
     }
     // key 相同,但是 type 不相同,删除 fiber 以及兄弟节点
     deleteRemainingChildren(returnFiber, child);
     break;
     } else {
     // key 不相同,不能复用,删除
     deleteChild(returnFiber, child);
     }
     // 同级兄弟节点
     child = child.sibling;
     }
     // 新建 fiber 并返回
     if (element.type === REACT_FRAGMENT_TYPE) {
     const created = createFiberFromFragment(
     element.props.children,
     returnFiber.mode,
     lanes,
     element.key
     );
     created.return = returnFiber;
     return created;
     } else {
     const created = createFiberFromElement(element, returnFiber.mode, lanes);
     created.ref = coerceRef(returnFiber, currentFirstChild, element);
     created.return = returnFiber;
     return created;
     }
    }
    
    • 多节点 diff:需要处理节点更新、节点新增/减少、节点位置变化
     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    
    function reconcileChildrenArray(
     returnFiber,
     currentFirstChild,
     newChildren,
     lanes
    ) {
     /**
     * 这个算法不能通过从两端搜索来优化,和 newChildren 中每个组件相比较的是 currentFiber
     * newChildren[0] 与 fiber 比较,newChildren[1] 与 fiber.sibling 比较
     */
     let resultingFirstChild = null;
     let previousNewFiber = null;
     let oldFiber = currentFirstChild;
     let lastPlacedIndex = 0;
     let newIdx = 0;
     let nextOldFiber = null;
     // 首先遍历 newChildren
     for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
     if (oldFiber.index > newIdx) {
     nextOldFiber = oldFiber;
     oldFiber = null;
     } else {
     nextOldFiber = oldFiber.sibling;
     }
     const newFiber = updateSlot(
     returnFiber,
     oldFiber,
     newChildren[newIdx],
     lanes
     );
     // newChildren 与 oldFiber 同时遍历完了
     if (newFiber === null) {
     if (oldFiber === null) {
     oldFiber = nextOldFiber;
     }
     break;
     }
     if (shouldTrackSideEffects) {
     if (oldFiber && newFiber.alternate === null) {
     deleteChild(returnFiber, oldFiber);
     }
     }
     lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
     if (previousNewFiber === null) {
     resultingFirstChild = newFiber;
     } else {
     previousNewFiber.sibling = newFiber;
     }
     previousNewFiber = newFiber;
     oldFiber = nextOldFiber;
     }
     // newChildren 遍历完了,删除剩余的 oldFiber
     if (newIdx === newChildren.length) {
     deleteRemainingChildren(returnFiber, oldFiber);
     return resultingFirstChild;
     }
     // oldFiber 遍历完了,插入剩余的 newChildren
     if (oldFiber === null) {
     for (; newIdx < newChildren.length; newIdx++) {
     const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
     if (newFiber === null) {
     continue;
     }
     lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
     if (previousNewFiber === null) {
     resultingFirstChild = newFiber;
     } else {
     previousNewFiber.sibling = newFiber;
     }
     previousNewFiber = newFiber;
     }
     return resultingFirstChild;
     }
     // newChildren 与 oldFiber 都没遍历完,将没有处理的 oldFiber 存入 map
     const existingChildren = mapRemainingChildren(returnFiber, oldFiber);
     // 遍历剩余的 newChildren,移动节点
     for (; newIdx < newChildren.length; newIdx++) {
     const newFiber = updateFromMap(
     existingChildren,
     returnFiber,
     newIdx,
     newChildren[newIdx],
     lanes
     );
     if (newFiber !== null) {
     if (shouldTrackSideEffects) {
     if (newFiber.alternate !== null) {
     existingChildren.delete(
     newFiber.key === null ? newIdx : newFiber.key
     );
     }
     }
     // 最后一个可复用的节点在 oldFiber 中的位置索引
     lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
     if (previousNewFiber === null) {
     resultingFirstChild = newFiber;
     } else {
     previousNewFiber.sibling = newFiber;
     }
     previousNewFiber = newFiber;
     }
     }
     if (shouldTrackSideEffects) {
     // 没有消耗的已经存在的 children 都删掉
     existingChildren.forEach(child => deleteChild(returnFiber, child));
     }
     return resultingFirstChild;
    }
    

    fiber

    没有 fiber 以前,reconciler 采用递归的方式创建虚拟 DOM,这个过程是不能中断的;如果组件树的层级很深,递归会占用线程很多时间,阻塞渲染造成卡顿。为了解决这个问题,React 引入 fiber,重构为了异步的可中断更新:

    • fiber 的含义:
      • 递归的数据保存在调用栈中,所以被称为 StackReconciler;基于 fiber 的 Reconciler,被称为 FiberReconciler;
      • 作为静态的数据结构来说,每个 fiber 节点对应一个 React 元素,保存了该组件的类型(函数组件/类组件/原生组件…)、对应的 DOM 节点等信息;
      • 作为动态的工作单元来说,每个 fiber 节点保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新…)。
    • fiber 的目标:
      • 能够把可中断的任务切片处理;
      • 能够调整优先级,重置并复用任务;
      • 能够在父元素与子元素之间交错处理;
      • 能够在 render 中返回多个元素;
      • 更好地支持错误边界。
    • fiberNode 的生成:通过 createFiber 工厂函数生成 fiberNode 实例;
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    
    const createFiber = function (
     tag: WorkTag,
     pendingProps: mixed,
     key: null | string,
     mode: TypeOfMode
    ): Fiber {
     return new FiberNode(tag, pendingProps, key, mode);
    };
    function FiberNode(
     tag: WorkTag,
     pendingProps: any,
     key: null | string,
     mode: TypeOfMode
    ) {
     // 作为静态数据结构的属性
     this.tag = tag; // 对应的组件类型
     this.key = key; // key 属性
     this.elementType = null; // 元素类型,大部分情况同 type,某些情况不同,比如 React.memo(FunctionComponent)
     this.type = null; // 对于函数组件,指函数本身;对于类组件,指 class;对于原生组件,指 DOM 节点的 tagName;用来描述与 fiber 相关的 React 元素
     this.stateNode = null; // 对应的 DOM、FiberRoot、ComponentInstance;保存了 fiber 相关的局部状态
     // 用于连接其他 FiberNode 生成『树』形结构
     this.return = null; // 指向父级 FiberNode
     this.child = null; // 指向子级 FiberNode
     this.sibling = null; // 指向兄弟 FiberNode
     this.index = 0;
     // 保存本次更新造成的状态改变相关信息,用来计算 state
     this.pendingProps = pendingProps; // 新的 props,需要应用到子组件或者 DOM 元素上
     this.memoizedProps = null; // 上次渲染期间对应的 props
     this.memoizedState = null; // 经过所有流程处理后的新 state;在处理更新的过程中,对应的是屏幕上展示的状态
     this.updateQueue = null; // 更新队列(保存了 stateUpdates、回调、DOMUpdates),链表结构,用于计算本次的 state
     this.dependencies = null;
     // 保存本次更新会造成的 DOM 操作,需要在 commit 阶段应用到视图上
     this.flags = NoFlags;
     this.subtreeFlags = NoFlags;
     this.deletions = null;
     // 调度优先级相关
     this.lanes = NoLanes;
     this.childLanes = NoLanes;
     /**
     * 双缓存:指向 current/workInProgress 中对应的 fiber 节点
     * currentFiber.alternate === workInProgressFiber;
     * workInProgressFiber.alternate === currentFiber
     */
     this.alternate = null; // 双缓存相关
     this.mode = mode;
     this.ref = null;
    }
    
    • fiberTree 的生成:通过 performUnitOfWork(分为两部分:“递”和“归”)向 workInProgress 添加 fiber 节点;
     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    
    function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
     /**
     * 这个方法用来为 fiberRoot 调度任务;每个 fiberRoot 每次只有一个任务
     * 如果一个任务已经被调度,将检查以确保现有任务的优先级与下一个级别的优先级相同
     * 此函数在每次更新时调用,并且调用恰好发生在退出任务之前
     */
     const existingCallbackNode = root.callbackNode;
     // 检查是否有 lane 处于饥饿状态,如果有就把它标记为过期任务,需要下次更新时执行
     markStarvedLanesAsExpired(root, currentTime);
     // 检测下一个要处理的 lane 和对应的优先级
     const nextLanes = getNextLanes(
     root,
     root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
     );
     // 返回在“getNextLanes”调用期间计算的优先级
     const newCallbackPriority = returnNextLanesPriority();
     if (nextLanes === NoLanes) {
     if (existingCallbackNode !== null) {
     cancelCallback(existingCallbackNode);
     root.callbackNode = null;
     root.callbackPriority = NoLanePriority;
     }
     return;
     }
     // 对现存任务检查,看是否能被复用
     if (existingCallbackNode !== null) {
     const existingCallbackPriority = root.callbackPriority;
     if (existingCallbackPriority === newCallbackPriority) {
     // 优先级没改变,可以重复使用
     return;
     }
     // 不能复用任务
     cancelCallback(existingCallbackNode);
     }
     // 调度回调
     let newCallbackNode;
     if (newCallbackPriority === SyncLanePriority) {
     // 特殊情况:同步任务在内部队列里被调度
     newCallbackNode = scheduleSyncCallback(
     performSyncWorkOnRoot.bind(null, root)
     );
     } else if (newCallbackPriority === SyncBatchedLanePriority) {
     // 批量同步任务
     newCallbackNode = scheduleCallback(
     ImmediateSchedulerPriority,
     performSyncWorkOnRoot.bind(null, root)
     );
     } else {
     // 并发调用
     const schedulerPriorityLevel =
     lanePriorityToSchedulerPriority(newCallbackPriority);
     newCallbackNode = scheduleCallback(
     schedulerPriorityLevel,
     performConcurrentWorkOnRoot.bind(null, root)
     );
     }
     root.callbackPriority = newCallbackPriority;
     root.callbackNode = newCallbackNode;
    }
    // ensureRootIsScheduled 调用 performSyncWorkOnRoot 调用 renderRootSync 调用 workLoopSync
    function workLoopSync() {
     // 已经超时了,不检查当前帧是否还有时间,执行任务
     while (workInProgress !== null) {
     performUnitOfWork(workInProgress);
     }
    }
    // ensureRootIsScheduled 调用 performConcurrentWorkOnRoot(递归调用自己)调用 renderRootConcurrent 调用 workLoopConcurrent
    function workLoopConcurrent() {
     // 如果当前帧还有时间,就执行任务
     while (workInProgress !== null && !shouldYield()) {
     performUnitOfWork(workInProgress);
     }
    }
    function performUnitOfWork(unitOfWork: Fiber): void {
     const current = unitOfWork.alternate;
     // beginWork 总是返回传入节点的子节点
     let next = beginWork(current, unitOfWork, subtreeRenderLanes);
     unitOfWork.memoizedProps = unitOfWork.pendingProps;
     if (next === null) {
     // 子节点遍历完了,开始遍历当前子树的兄弟节点并回溯到父节点
     completeUnitOfWork(unitOfWork);
     } else {
     workInProgress = next;
     }
     ReactCurrentOwner.current = null;
    }
    function completeUnitOfWork(unitOfWork: Fiber): void {
     // 完成当前任务,并移至兄弟节点;如果没有兄弟节点,回到父级节点
     let completedWork = unitOfWork;
     do {
     const current = completedWork.alternate;
     const returnFiber = completedWork.return;
     // 检测任务是否完成
     if ((completedWork.flags & Incomplete) === NoFlags) {
     let next;
     if ((completedWork.mode & ProfileMode) === NoMode) {
     next = completeWork(current, completedWork, subtreeRenderLanes);
     }
     if (next !== null) {
     workInProgress = next;
     return;
     }
     } else {
     const next = unwindWork(completedWork, subtreeRenderLanes);
     if (next !== null) {
     next.flags &= HostEffectMask;
     workInProgress = next;
     return;
     }
     if (returnFiber !== null) {
     returnFiber.flags |= Incomplete;
     returnFiber.subtreeFlags = NoFlags;
     returnFiber.deletions = null;
     }
     }
     const siblingFiber = completedWork.sibling;
     if (siblingFiber !== null) {
     // 指向兄弟节点,开始兄弟节点的『递和归』
     workInProgress = siblingFiber;
     return;
     }
     // 指向父节点,完成父节点的『归』
     completedWork = returnFiber;
     workInProgress = completedWork;
     } while (completedWork !== null);
     if (workInProgressRootExitStatus === RootIncomplete) {
     workInProgressRootExitStatus = RootCompleted;
     }
    }
    
    • “递”阶段(beginWork):传入当前 fiberNode,创建子 fiberNode;
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    
    /**
     * 从 rootFiber 开始向下深度优先遍历;为遍历到的每个 fiberNode 调用 beginWork 方法
     * 该方法会根据传入的 fiberNode 创建子 fiberNode,并将这两个 fiberNode 连接起来
     * 当遍历到“没有子组件的组件”时就会进入“归”阶段
     */
    function beginWork(
     current, // 上一次更新的 fiberNode(workInProgress.alternate)
     workInProgress, // 当前组件对应的 fiberNode
     renderLanes // 渲染优先级
    ) {
     const updateLanes = workInProgress.lanes;
     if (current !== null) {
     // update 阶段,可以复用节点
     const oldProps = current.memoizedProps;
     const newProps = workInProgress.pendingProps;
     /**
     * 节点不能复用的条件(满足任一就不能复用):
     * oldProps !== newProps
     * hasContextChanged()
     * workInProgress.type !== current.type
     */
     if (
     oldProps !== newProps ||
     hasLegacyContextChanged() ||
     workInProgress.type !== current.type
     ) {
     didReceiveUpdate = true; // type 都不一样,复用个鸡
     } else if (!includesSomeLane(renderLanes, updateLanes)) {
     // 当前节点优先级不够
     didReceiveUpdate = false; // 可以复用子节点
     switch (workInProgress.tag) {
     case HostRoot: // ...
     case HostComponent: // ...
     case ClassComponent: // ...
     case HostPortal: // ...
     case ContextProvider: // ...
     case Profiler: // ...
     case SuspenseComponent: // ...
     case SuspenseListComponent: // ...
     case OffscreenComponent: // ...
     case LegacyHiddenComponent: // ...
     }
     return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
     } else {
     if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
     didReceiveUpdate = true;
     } else {
     didReceiveUpdate = false; // 可以复用子节点
     }
     }
     } else {
     // mount 阶段,根据 tag 创建节点
     didReceiveUpdate = false;
     }
     workInProgress.lanes = NoLanes;
     // 根据 tag 创建不同的 fiber,最终会调用 reconcileChildren
     switch (workInProgress.tag) {
     case IndeterminateComponent: // ...
     case LazyComponent: // ...
     case FunctionComponent: // ...
     case ClassComponent: // ...
     case HostRoot: // ...
     case HostComponent: // ...
     case HostText: // ...
     case SuspenseComponent: // ...
     case HostPortal: // ...
     case ForwardRef: // ...
     case Fragment: // ...
     case Mode: // ...
     case Profiler: // ...
     case ContextProvider: // ...
     case ContextConsumer: // ...
     case MemoComponent: // ...
     case SimpleMemoComponent: // ...
     case IncompleteClassComponent: // ...
     case SuspenseListComponent: // ...
     case FundamentalComponent: // ...
     case ScopeComponent: // ...
     case Block: // ...
     case OffscreenComponent: // ...
     case LegacyHiddenComponent: // ...
     }
    }
    
    • reconcileChildren:reconcileChildFibers 与 mountChildFibers 的区别是会为 fiber 节点打上 effectTag;
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    export function reconcileChildren(
     current,
     workInProgress,
     nextChildren,
     renderLanes
    ) {
     if (current === null) {
     /**
     * mount 阶段,创建子 fiberNode
     * 如果这是一个还没有被渲染的新组件
     * 我们不会通过应用最小的副作用来更新它的子集
     * 相反,我们会在子集被渲染之前将其全部加入
     * 这意味着我们可以通过不跟踪副作用来优化这个调和通道
     */
     workInProgress.child = mountChildFibers(
     workInProgress,
     null,
     nextChildren,
     renderLanes
     );
     } else {
     /**
     * update 阶段,diff 两棵树上的 fiberNode,进行复用
     * reconcileChildFibers 会生成带有 effectTag 属性的 fiberNode
     * 如果当前的 current 子节点和 workInProgress 中的相同
     * 这说明我们还没有开始对这些子节点进行任何操作
     * 所以,我们使用克隆算法来创建所有 children
     */
     workInProgress.child = reconcileChildFibers(
     workInProgress,
     current.child,
     nextChildren,
     renderLanes
     );
     }
    }
    // 通过 shouldTrackSideEffects 来决定是否打上 effectTag
    export const mountChildFibers = ChildReconciler(false);
    export const reconcileChildFibers = ChildReconciler(true);
    
    • effectTag:在 commit 阶段告知 renderer 对视图进行何种操作;在首次渲染时,只有 rootFiber 会被打上 Placement,在 commit 阶段进行一次插入操作;
    1
    2
    3
    4
    5
    
    export const Placement = /* */ 0b000000000000000010;
    export const Update = /* */ 0b000000000000000100;
    export const PlacementAndUpdate = /* */ 0b000000000000000110;
    export const Deletion = /* */ 0b000000000000001000;
    // ...
    
    • “归”阶段(completeWork):
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    
    /**
     * 当某个 fiberNode 执行完 completeWork,如果其存在兄弟 fiberNode(fiber.sibling !== null),会进入其兄弟 fiberNode 的“递”阶段
     * 如果不存在兄弟 fiberNode,会进入父级 fiber 的“归”阶段
     * “递”和“归”阶段会交错执行,直到“归”到 rootFiber
     */
    function completeWork(current, workInProgress, renderLanes) {
     const newProps = workInProgress.pendingProps;
     switch (workInProgress.tag) {
     case IndeterminateComponent:
     case LazyComponent:
     case SimpleMemoComponent:
     case FunctionComponent:
     case ForwardRef:
     case Fragment:
     case Mode:
     case Profiler:
     case ContextConsumer:
     case MemoComponent:
     return null;
     case ClassComponent: {
     // ...
     return null;
     }
     case HostRoot: {
     // ...
     updateHostContainer(workInProgress);
     return null;
     }
     case HostComponent: {
     popHostContext(workInProgress);
     const rootContainerInstance = getRootHostContainer();
     const type = workInProgress.type;
     // 需要考虑 fiberNode 是否有对应的 DOM 节点
     if (current !== null && workInProgress.stateNode != null) {
     // update 阶段,处理 props
     updateHostComponent(
     current,
     workInProgress,
     type,
     newProps,
     rootContainerInstance
     );
     // ...
     } else {
     // mount 阶段
     const currentHostContext = getHostContext();
     // 为 fiberNode 生成对应的 DOM 节点
     const instance = createInstance(
     type,
     newProps,
     rootContainerInstance,
     currentHostContext,
     workInProgress
     );
     /**
     * 将子孙 DOM 节点插入刚生成的 DOM 节点中
     * 每次调用 appendAllChildren 时都会将已生成的子孙 DOM 节点插入当前生成的 DOM 节点下
     * 那么当“归”到 rootFiber 时,我们已经有一个构建好的离屏 DOM 树
     */
     appendAllChildren(instance, workInProgress, false, false);
     // DOM 节点赋值给 fiber.stateNode
     workInProgress.stateNode = instance;
     // 处理 props
     if (
     finalizeInitialChildren(
     instance,
     type,
     newProps,
     rootContainerInstance,
     currentHostContext
     )
     ) {
     markUpdate(workInProgress);
     }
     // ...
     }
     return null;
     }
     // ...
     }
    }
    
    • effectList:除了构建 workInProgress,completeUnitOfWork 还会将所有存在 effectTag 中的 fiberNode 保存在 effectList 单向链表中(nextEffect 指针指向下一个 fiber,第一个节点为 firstEffect,最后一个节点为 lastEffect),在 commit 阶段,只需要遍历 effectList,执行所有 effect,执行对应的 DOM 操作就好。
    1
    2
    3
    
     nextEffect nextEffect nextEffect
    
    rootFiber.firstEffect ----------> fiber ----------> fiber ----------> ...
    

    render 阶段之后 fiberRoot 会被传递给 commitRoot,进入 commit 阶段。

    hooks

    在 render 的 begin(递)的阶段,会在 mountIndeterminateComponent 中挂载组件:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    
    function mountIndeterminateComponent(
     _current,
     workInProgress,
     Component,
     renderLanes
    ) {
     // ...
     let value;
     value = renderWithHooks(
     null,
     workInProgress,
     Component,
     props,
     context,
     renderLanes
     );
     workInProgress.flags |= PerformedWork;
     if (
     !disableModulePatternComponents &&
     typeof value === 'object' &&
     value !== null &&
     typeof value.render === 'function' &&
     value.$$typeof === undefined
     ) {
     // 类组件逻辑...
     } else {
     // 函数组件逻辑...
     workInProgress.tag = FunctionComponent;
     reconcileChildren(null, workInProgress, value, renderLanes);
     return workInProgress.child;
     }
    }
    // 函数式组件的入口
    export function renderWithHooks<Props, SecondArg>(
     current: Fiber | null,
     workInProgress: Fiber,
     Component: (p: Props, arg: SecondArg) => any,
     props: Props,
     secondArg: SecondArg,
     nextRenderLanes: Lanes
    ): any {
     renderLanes = nextRenderLanes;
     currentlyRenderingFiber = workInProgress;
     /**
     * 类组件中,memoizedState 存放 state
     * 函数组件中,memoizedState 以链表的形式存放 hook
     */
     workInProgress.memoizedState = null;
     workInProgress.updateQueue = null;
     workInProgress.lanes = NoLanes;
     // 只有当一个有状态的 hook 被使用时,才可以通过 memoizedState 来区分是 mount 还是 update
     ReactCurrentDispatcher.current =
     current === null || current.memoizedState === null
     ? HooksDispatcherOnMount // mount 时用的 hook
     : HooksDispatcherOnUpdate; // update 时用的 hook
     let children = Component(props, secondArg); // 调用函数组件,组件内 hook 执行,保存到 WIP 上
     // 检测渲染阶段的 update
     if (didScheduleRenderPhaseUpdateDuringThisPass) {
     // ...
     }
     ReactCurrentDispatcher.current = ContextOnlyDispatcher;
     const didRenderTooFewHooks =
     currentHook !== null && currentHook.next !== null;
     renderLanes = NoLanes;
     currentlyRenderingFiber = null;
     currentHook = null;
     workInProgressHook = null;
     didScheduleRenderPhaseUpdate = false;
     return children;
    }
    

    内置的 hooks 都是从 ReactCurrentDispatcher.current 上拿的;组件不同阶段,给 ReactCurrentDispatcher.current 赋不同的值:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
    function resolveDispatcher() {
     const dispatcher = ReactCurrentDispatcher.current;
     invariant(dispatcher !== null, 'Hooks 只能在函数组件里调用');
     return dispatcher;
    }
    export function useState<S>(
     initialState: (() => S) | S
    ): [S, Dispatch<BasicStateAction<S>>] {
     const dispatcher = resolveDispatcher();
     return dispatcher.useState(initialState);
    }
    export function useRef<T>(initialValue: T): { current: T } {
     const dispatcher = resolveDispatcher();
     return dispatcher.useRef(initialValue);
    }
    export function useEffect(
     create: () => (() => void) | void,
     deps: Array<mixed> | void | null
    ): void {
     const dispatcher = resolveDispatcher();
     return dispatcher.useEffect(create, deps);
    }
    export function useMemo<T>(
     create: () => T,
     deps: Array<mixed> | void | null
    ): T {
     const dispatcher = resolveDispatcher();
     return dispatcher.useMemo(create, deps);
    }
    const HooksDispatcherOnMount: Dispatcher = {
     readContext,
     useCallback: mountCallback,
     useContext: readContext,
     useEffect: mountEffect,
     useLayoutEffect: mountLayoutEffect,
     useMemo: mountMemo,
     useReducer: mountReducer,
     useRef: mountRef,
     useState: mountState
     // ...
    };
    const HooksDispatcherOnUpdate: Dispatcher = {
     readContext,
     useCallback: updateCallback,
     useContext: readContext,
     useEffect: updateEffect,
     useLayoutEffect: updateLayoutEffect,
     useMemo: updateMemo,
     useReducer: updateReducer,
     useRef: updateRef,
     useState: updateState
     // ...
    };
    

    函数组件在挂载阶段,内部的每个 hook 都会产生一个对象绑定在 workInProgress.memoizedState 属性上,并形成循环链表结构;挂载阶段对应的 hooks:

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    
    function mountState<S>(
     initialState: (() => S) | S
    ): [S, Dispatch<BasicStateAction<S>>] {
     const hook = mountWorkInProgressHook();
     // 如果初始状态是一个函数,函数的返回值作为初始状态
     if (typeof initialState === 'function') {
     initialState = initialState();
     }
     // 保存初始状态
     hook.memoizedState = hook.baseState = initialState;
     const queue = (hook.queue = {
     pending: null,
     dispatch: null, // 提交 action 的函数
     lastRenderedReducer: basicStateReducer, // 用于状态更新的 reducer
     lastRenderedState: initialState // 自从上次渲染过后,最新的 state
     });
     const dispatch: Dispatch<BasicStateAction<S>> = (queue.dispatch =
     dispatchAction.bind(null, currentlyRenderingFiber, queue));
     return [hook.memoizedState, dispatch];
    }
    // 组件第一次渲染,hooks 都会调用 mountWorkInProgressHook
    function mountWorkInProgressHook(): Hook {
     const hook: Hook = {
     memoizedState: null, // 保存 hooks 信息(state、effect、dep、ref 等)
     baseState: null, // 当前最新 state
     baseQueue: null, // 当前最新更新队列
     queue: null, // 待更新队列
     next: null // 链表指针,链表的存在导致 hooks 不能在条件语句中调用
     };
     if (workInProgressHook === null) {
     currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
     } else {
     workInProgressHook = workInProgressHook.next = hook;
     }
     return workInProgressHook;
    }
    // mountState 内的 dispatch
    function dispatchAction<S, A>(
     fiber: Fiber,
     queue: UpdateQueue<S, A>,
     action: A
    ) {
     const eventTime = requestEventTime();
     const lane = requestUpdateLane(fiber);
     const update: Update<S, A> = {
     lane,
     action, // 新提交的状态
     eagerReducer: null,
     eagerState: null,
     next: null
     };
     // 将更新添加到链表最后
     const pending = queue.pending;
     if (pending === null) {
     // 这是第一个更新,创建循环链表
     update.next = update;
     } else {
     update.next = pending.next;
     pending.next = update;
     }
     queue.pending = update;
     const alternate = fiber.alternate;
     if (
     fiber === currentlyRenderingFiber ||
     (alternate !== null && alternate === currentlyRenderingFiber)
     ) {
     // 发生在渲染阶段的更新
     didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =
     true;
     } else {
     if (
     fiber.lanes === NoLanes &&
     (alternate === null || alternate.lanes === NoLanes)
     ) {
     /**
     * 队列当前是空的;在进入下一次渲染之前,可以计算下一个状态
     * 如果新的状态和当前状态一致,可以跳过更新(两次 useState 值相等就不渲染)
     */
     const lastRenderedReducer = queue.lastRenderedReducer;
     if (lastRenderedReducer !== null) {
     let prevDispatcher;
     try {
     const currentState: S = queue.lastRenderedState;
     const eagerState = lastRenderedReducer(currentState, action);
     update.eagerReducer = lastRenderedReducer;
     update.eagerState = eagerState;
     if (is(eagerState, currentState)) {
     return;
     }
     } catch (error) {
     } finally {
     }
     }
     }
     // 调度更新
     scheduleUpdateOnFiber(fiber, lane, eventTime);
     }
    }
    function mountEffect(
     create: () => (() => void) | void,
     deps: Array<mixed> | void | null
    ): void {
     // 内部也会调用 mountWorkInProgressHook
     return mountEffectImpl(
     PassiveEffect | PassiveStaticEffect,
     HookPassive,
     create,
     deps
     );
    }
    // mountEffect 内的 mountEffectImpl 还是会调用 mountWorkInProgressHook
    function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
     const hook = mountWorkInProgressHook();
     const nextDeps = deps === undefined ? null : deps;
     currentlyRenderingFiber.flags |= fiberFlags;
     hook.memoizedState = pushEffect(
     HookHasEffect | hookFlags,
     create, // 副作用函数
     undefined,
     nextDeps // Dep 依赖
     );
    }
    // mountEffect 内的 mountEffectImpl 也会调用 pushEffect
    function pushEffect(tag, create, destroy, deps) {
     const effect: Effect = {
     tag,
     create,
     destroy,
     deps,
     next: null,
     };
     let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);
     if (componentUpdateQueue === null) {
     // 第一个 useEffect,创建更新队列
     componentUpdateQueue = createFunctionComponentUpdateQueue();
     currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any);
     componentUpdateQueue.lastEffect = effect.next = effect;
     } else {
     // 多个 useEffect
     const lastEffect = componentUpdateQueue.lastEffect;
     if (lastEffect === null) {
     componentUpdateQueue.lastEffect = effect.next = effect;
     } else {
     const firstEffect = lastEffect.next;
     lastEffect.next = effect;
     effect.next = firstEffect;
     componentUpdateQueue.lastEffect = effect;
     }
     }
     return effect;
    }
    function mountMemo<T>(
     nextCreate: () => T,
     deps: Array<mixed> | void | null
    ): T {
     const hook = mountWorkInProgressHook(); // 创建一个 hook
     const nextDeps = deps === undefined ? null : deps;
     const nextValue = nextCreate(); // 获取传入函数的返回值
     hook.memoizedState = [nextValue, nextDeps]; // 在 hook 上保存新的值就好
     return nextValue;
    }
    function mountRef<T>(initialValue: T): { current: T } {
     const hook = mountWorkInProgressHook();
     // 保存初始化的值,反正后续更新,需要手动给 current 赋值
     const ref = { current: initialValue };
     hook.memoizedState = ref;
     return ref;
    }
    

    更新阶段对应的 hooks:

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    
    // 在更新或者渲染阶段,更新触发的重新渲染都会调用此函数
    function updateWorkInProgressHook(): Hook {
     let nextCurrentHook: null | Hook;
     if (currentHook === null) {
     const current = currentlyRenderingFiber.alternate;
     if (current !== null) {
     nextCurrentHook = current.memoizedState;
     } else {
     nextCurrentHook = null;
     }
     } else {
     nextCurrentHook = currentHook.next;
     }
     let nextWorkInProgressHook: null | Hook;
     if (workInProgressHook === null) {
     nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
     } else {
     nextWorkInProgressHook = workInProgressHook.next;
     }
     if (nextWorkInProgressHook !== null) {
     // 复用
     workInProgressHook = nextWorkInProgressHook;
     nextWorkInProgressHook = workInProgressHook.next;
     currentHook = nextCurrentHook;
     } else {
     currentHook = nextCurrentHook;
     const newHook: Hook = {
     memoizedState: currentHook.memoizedState,
     baseState: currentHook.baseState,
     baseQueue: currentHook.baseQueue,
     queue: currentHook.queue,
     next: null
     };
     if (workInProgressHook === null) {
     currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
     } else {
     workInProgressHook = workInProgressHook.next = newHook;
     }
     }
     return workInProgressHook;
    }
    function updateState<S>(
     initialState: (() => S) | S
    ): [S, Dispatch<BasicStateAction<S>>] {
     // 内部也会调用 updateWorkInProgressHook
     return updateReducer(basicStateReducer, initialState);
    }
    function updateReducer<S, I, A>(
     reducer: (S, A) => S,
     initialArg: I,
     init?: (I) => S
    ): [S, Dispatch<A>] {
     const hook = updateWorkInProgressHook();
     const queue = hook.queue;
     queue.lastRenderedReducer = reducer;
     const current: Hook = currentHook;
     let baseQueue = current.baseQueue;
     // 上次还没处理完的待处理更新
     const pendingQueue = queue.pending;
     if (pendingQueue !== null) {
     // 还没来得及处理的新的更新,将他们添加到『基』队列
     if (baseQueue !== null) {
     // 合并待处理队列和『基』队列
     const baseFirst = baseQueue.next;
     const pendingFirst = pendingQueue.next;
     baseQueue.next = pendingFirst;
     pendingQueue.next = baseFirst;
     }
     current.baseQueue = baseQueue = pendingQueue;
     queue.pending = null;
     }
     if (baseQueue !== null) {
     // 有个队列需要处理
     const first = baseQueue.next;
     let newState = current.baseState;
     let newBaseState = null;
     let newBaseQueueFirst = null;
     let newBaseQueueLast = null;
     let update = first;
     do {
     const updateLane = update.lane;
     if (!isSubsetOfLanes(renderLanes, updateLane)) {
     // 优先级不够,跳过更新
     const clone: Update<S, A> = {
     lane: updateLane,
     action: update.action,
     eagerReducer: update.eagerReducer,
     eagerState: update.eagerState,
     next: null
     };
     if (newBaseQueueLast === null) {
     newBaseQueueFirst = newBaseQueueLast = clone;
     newBaseState = newState;
     } else {
     newBaseQueueLast = newBaseQueueLast.next = clone;
     }
     // 更新队列优先级
     currentlyRenderingFiber.lanes = mergeLanes(
     currentlyRenderingFiber.lanes,
     updateLane
     );
     markSkippedUpdateLanes(updateLane);
     } else {
     // 更新有足够的优先级
     if (newBaseQueueLast !== null) {
     const clone: Update<S, A> = {
     // 更新将被提交
     lane: NoLane, // 所有掩码的子集
     action: update.action,
     eagerReducer: update.eagerReducer,
     eagerState: update.eagerState,
     next: null
     };
     newBaseQueueLast = newBaseQueueLast.next = clone;
     }
     // 处理更新
     if (update.eagerReducer === reducer) {
     newState = update.eagerState;
     } else {
     const action = update.action;
     newState = reducer(newState, action);
     }
     }
     update = update.next;
     } while (update !== null && update !== first);
     if (newBaseQueueLast === null) {
     newBaseState = newState;
     } else {
     newBaseQueueLast.next = newBaseQueueFirst;
     }
     if (!is(newState, hook.memoizedState)) {
     markWorkInProgressReceivedUpdate();
     }
     hook.memoizedState = newState;
     hook.baseState = newBaseState;
     hook.baseQueue = newBaseQueueLast;
     queue.lastRenderedState = newState;
     }
     const dispatch: Dispatch<A> = queue.dispatch;
     return [hook.memoizedState, dispatch];
    }
    function updateEffect(
     create: () => (() => void) | void,
     deps: Array<mixed> | void | null
    ): void {
     // 内部也会调用 updateWorkInProgressHook
     return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
    }
    function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
     const hook = updateWorkInProgressHook();
     const nextDeps = deps === undefined ? null : deps;
     let destroy = undefined;
     if (currentHook !== null) {
     const prevEffect = currentHook.memoizedState;
     destroy = prevEffect.destroy;
     if (nextDeps !== null) {
     const prevDeps = prevEffect.deps;
     if (areHookInputsEqual(nextDeps, prevDeps)) {
     // 相等就不更新
     pushEffect(hookFlags, create, destroy, nextDeps);
     return;
     }
     }
     }
     currentlyRenderingFiber.flags |= fiberFlags;
     hook.memoizedState = pushEffect(
     HookHasEffect | hookFlags,
     create,
     destroy,
     nextDeps
     );
    }
    function updateMemo<T>(
     nextCreate: () => T,
     deps: Array<mixed> | void | null
    ): T {
     const hook = updateWorkInProgressHook();
     const nextDeps = deps === undefined ? null : deps;
     const prevState = hook.memoizedState;
     if (prevState !== null) {
     if (nextDeps !== null) {
     const prevDeps: Array<mixed> | null = prevState[1];
     if (areHookInputsEqual(nextDeps, prevDeps)) {
     // 相等就返回上一个状态
     return prevState[0];
     }
     }
     }
     // 重新计算新值
     const nextValue = nextCreate();
     hook.memoizedState = [nextValue, nextDeps];
     return nextValue;
    }
    function updateRef<T>(initialValue: T): { current: T } {
     const hook = updateWorkInProgressHook();
     return hook.memoizedState;
    }
    

    组件类型

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    // 有状态组件
    export const ClassComponent = 1;
    export const ContextConsumer = 9;
    export const ContextProvider = 10;
    // 无状态组件
    export const FunctionComponent = 0;
    export const IndeterminateComponent = 2; // 不确定(是函数还是类)组件(一个函数返回了一个带 render 方法与其他生命周期的对象)
    export const ForwardRef = 11;
    export const MemoComponent = 14;
    export const SimpleMemoComponent = 15; // MemoComponent 退化过来的,指没有自定义比较方法的 MemoComponent
    // 原生组件
    export const HostRoot = 3; // 渲染起点,可以嵌套在别的节点
    export const HostPortal = 4; // 切换渲染起点,可以将子节点渲染到到父节点之外的 DOM 节点上
    export const HostComponent = 5; // 元素节点
    export const HostText = 6; // 文本节点
    // 虚拟组件
    export const Fragment = 7; // 数组的语法糖,React 可以直接渲染数组
    export const Mode = 8; // 对应的 fiber 都通过 createFiberFromMode 生成的
    export const Profiler = 12; // 性能检测,配合 ReactDevTools 工作
    // 懒加载组件,有点像之前用的 ReactLoadable 的官方版
    export const SuspenseComponent = 13;
    export const LazyComponent = 16; // 返回 Promise
    export const SuspenseListComponent = 19;
    // 其他组件,我还没搞清楚
    export const IncompleteClassComponent = 17;
    export const DehydratedFragment = 18;
    export const FundamentalComponent = 20;
    export const ScopeComponent = 21;
    export const Block = 22;
    export const OffscreenComponent = 23; // React 版本的 keep-alive,保存失活组件状态
    export const LegacyHiddenComponent = 24;
    

    状态更新

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    
    // 初始化更新队列
    export function initializeUpdateQueue<State>(fiber: Fiber): void {
     /**
     * 假设 fiber 刚完成 commit 阶段
     * 有两个优先级很低的 update(u1、u2)在 render 阶段没有处理
     * u1、u2 就成为了下次更新的 baseUpdate
     * u1.next = u2,fiber.updateQueue.baseUpdate = u1 -> u2
     */
     const queue: UpdateQueue<State> = {
     baseState: fiber.memoizedState, // 更新前该 fiber 节点的 state
     /**
     * 更新前该 fiber 节点已保存的 update(链表)
     * 更新前就存在 update 是因为上次 render 阶段
     * 可能有优先级较低的 update 被跳过
     */
     firstBaseUpdate: u1, // 链表头,指向第一个 update 对象
     lastBaseUpdate: u2, // 链表尾,指向最后一个 update 对象
     shared: {
     /**
     * 触发更新时,产生的 update 保存在 shared.pending 中,形成单项环状链表
     * 在计算 state 时,链表被剪开,拼接在 lastBaseUpdate 后面
     * 假设触发了两次更新,u3、u4(u3 比 u4 先插入)
     * fiber.updateQueue.shared.pending === u4;
     * u4.next === u3;
     * u3.next === u4;
     * u3、u4 组成了一个环形链表
     * 在计算 state 时,shared.pending 被剪开并拼在 lastBaseUpdate 之后
     * 实际上 shared.pending 会被同时连接在(current/workInProgress)updateQueue.lastBaseUpdate 之后
     * u1 -> u2 -> u3 -> u4(可能会跳过优先级较低的 update),计算出本次更新的 state(memoizedState)
     */
     pending: null // 始终指向最后触发的更新 u4
     },
     effects: null // 保存 update.callback !== null 的 update
     };
     fiber.updateQueue = queue;
    }
    // setState 会调用 enqueueSetState 生成一个 update 并调度
    Component.prototype.setState = function (partialState, callback) {
     if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) {
     // ...
     }
     this.updater.enqueueSetState(this, partialState, callback, 'setState');
    };
    // 触发更新优先级
    enqueueSetState(inst, payload, callback) {
     // 通过组件实例获取对应 fiber
     const fiber = getInstance(inst);
     const eventTime = requestEventTime();
     const suspenseConfig = requestCurrentSuspenseConfig();
     // 获取优先级(根据事件的优先级来决定 update 优先级)
     const lane = requestUpdateLane(fiber, suspenseConfig);
     // 创建 update
     const update = createUpdate(eventTime, lane, suspenseConfig);
     update.payload = payload; // 部分 state
     // 确认回调函数,状态确定之后会调用
     if (callback !== undefined && callback !== null) {
     update.callback = callback;
     }
     // 将 setSate 产生的 update 插入 updateQueue
     enqueueUpdate(fiber, update);
     // 调度 update
     scheduleUpdateOnFiber(fiber, lane, eventTime);
    }
    // forceUpdate 会调用 enqueueForceUpdate 生成 update 并调度
    Component.prototype.forceUpdate = function(callback) {
     this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
    };
    enqueueForceUpdate(inst, callback) {
     const fiber = getInstance(inst);
     const eventTime = requestEventTime();
     const lane = requestUpdateLane(fiber);
     const update = createUpdate(eventTime, lane);
     update.tag = ForceUpdate; // 2,forceUpdate 没有 payload
     if (callback !== undefined && callback !== null) {
     update.callback = callback;
     }
     enqueueUpdate(fiber, update);
     scheduleUpdateOnFiber(fiber, lane, eventTime);
    };
    // update 结构(ClassComponent/HostRoot)
    export function createUpdate(eventTime, lane) {
     // 表示一个需要进行变更的动作
     const update = {
     // 若优先级不够导致 update 一直没执行;React 会在任务超时后,调度 update
     eventTime, // 任务时间(update 生成时间)
     lane, // update 优先级
     tag: UpdateState, // UpdateState、ReplaceState、ForceUpdate、CaptureUpdate
     payload: null, // 更新挂载的数据
     /**
     * 更新后的回调函数,如果这个回调有返回值
     * 就会在 updateQueue 的副作用链表中挂在当前 update
     * 具体逻辑在 processUpdateQueue 函数中
     */
     callback: null, // update 回调,setState 的第二个参数
     // 多次 setState 会有多个 update,他们之间通过 next 链接
     next: null // 指向下一个 update,所有 update 链接形成链表保存在 fiber.updateQueue
     };
     return update;
    }
    // 将 update 添加到 fiber 的 update 链表
    export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
     const updateQueue = fiber.updateQueue; // 获取 fiber 节点的 update 队列
     if (updateQueue === null) {
     // 当节点被卸载之后才会出现这种情况
     return;
     }
     const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
     const pending = sharedQueue.pending;
     if (pending === null) {
     // 链表是空的
     update.next = update;
     } else {
     update.next = pending.next;
     pending.next = update;
     }
     sharedQueue.pending = update;
    }
    export function scheduleUpdateOnFiber(
     fiber: Fiber,
     lane: Lane,
     eventTime: number
    ) {
     checkForNestedUpdates();
     // 从 fiber 遍历到 root
     const root = markUpdateLaneFromFiberToRoot(fiber, lane);
     if (root === null) {
     return null;
     }
     // 标记 root 有一个正在进行的更新
     markRootUpdated(root, lane, eventTime);
     if (root === workInProgressRoot) {
     /**
     * 如果渲染中间,收到了对树的更新
     * 那就是说,对于树,有交错更新的任务
     * 除非『推迟渲染阶段更新到下一个批处理』是 off,并且是渲染阶段的更新
     * 在这种情况下,出于向后兼容的原因
     * 不会将渲染阶段的更新视为要交错进行的任务
     */
     if (
     deferRenderPhaseUpdateToNextBatch ||
     (executionContext & RenderContext) === NoContext
     ) {
     workInProgressRootUpdatedLanes = mergeLanes(
     workInProgressRootUpdatedLanes,
     lane
     );
     }
     if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
     /**
     * root 已经挂起,渲染肯定没有结束
     * 既然有一个更新,在被标记为新的更新之前,那就先标记为『挂起』
     * 这会中断当前渲染并切换到新的更新
     */
     markRootSuspended(root, workInProgressRootRenderLanes);
     }
     }
     const priorityLevel = getCurrentPriorityLevel();
     if (lane === SyncLane) {
     if (
     // 检查是否在『unbatchedUpdates』
     (executionContext & LegacyUnbatchedContext) !== NoContext &&
     // 检查是否已经在渲染
     (executionContext & (RenderContext | CommitContext)) === NoContext
     ) {
     // root 的初始化渲染应该是同步的,布局更新应该推迟到批处理结束
     schedulePendingInteractions(root, lane);
     performSyncWorkOnRoot(root);
     } else {
     ensureRootIsScheduled(root, eventTime);
     schedulePendingInteractions(root, lane);
     if (executionContext === NoContext) {
     /**
     * 刷新同步工作,除非正在进行刷新或在批处理内
     * 这在『scheduleUpdateOnFiber』而不是在 scheduleCallbackForFiber 是故意的
     * 这会保留调度但不刷新回调的能力
     * 仅对用户发起的更新执行此操作,以保留传统模式的历史行为
     */
     resetRenderTimer();
     flushSyncCallbackQueue();
     }
     }
     } else {
     // 非同步的情况下,调度离散更新
     if (
     (executionContext & DiscreteEventContext) !== NoContext &&
     /**
     * 只有用户阻塞或更高优先级的更新才被视为离散的
     * 即使在离散事件中也是如此
     */
     (priorityLevel === UserBlockingSchedulerPriority ||
     priorityLevel === ImmediateSchedulerPriority)
     ) {
     // 追踪最低优先级的离散更新
     if (rootsWithPendingDiscreteUpdates === null) {
     rootsWithPendingDiscreteUpdates = new Set([root]);
     } else {
     rootsWithPendingDiscreteUpdates.add(root);
     }
     }
     ensureRootIsScheduled(root, eventTime);
     schedulePendingInteractions(root, lane);
     }
     mostRecentlyUpdatedRoot = root;
    }
    export function processUpdateQueue<State>(
     workInProgress: Fiber,
     props: any,
     instance: any,
     renderLanes: Lanes
    ): void {
     const queue: UpdateQueue<State> = workInProgress.updateQueue;
     hasForceUpdate = false;
     let firstBaseUpdate = queue.firstBaseUpdate;
     let lastBaseUpdate = queue.lastBaseUpdate;
     let pendingQueue = queue.shared.pending;
     if (pendingQueue !== null) {
     queue.shared.pending = null;
     // 剪开 pendingQueue 环状链表
     const lastPendingUpdate = pendingQueue;
     const firstPendingUpdate = lastPendingUpdate.next;
     lastPendingUpdate.next = null;
     // 将 pendingUpdate 添加到 baseQueue
     if (lastBaseUpdate === null) {
     firstBaseUpdate = firstPendingUpdate;
     } else {
     lastBaseUpdate.next = firstPendingUpdate;
     }
     lastBaseUpdate = lastPendingUpdate;
     const current = workInProgress.alternate;
     if (current !== null) {
     const currentQueue: UpdateQueue<State> = current.updateQueue;
     const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
     if (currentLastBaseUpdate !== lastBaseUpdate) {
     if (currentLastBaseUpdate === null) {
     currentQueue.firstBaseUpdate = firstPendingUpdate;
     } else {
     currentLastBaseUpdate.next = firstPendingUpdate;
     }
     currentQueue.lastBaseUpdate = lastPendingUpdate;
     }
     }
     }
     if (firstBaseUpdate !== null) {
     // 迭代更新列表以计算结果
     let newState = queue.baseState;
     let newLanes = NoLanes;
     let newBaseState = null;
     let newFirstBaseUpdate = null;
     let newLastBaseUpdate = null;
     let update = firstBaseUpdate;
     do {
     const updateLane = update.lane;
     const updateEventTime = update.eventTime;
     if (!isSubsetOfLanes(renderLanes, updateLane)) {
     // 优先级不够,跳过更新,跳过的更新的前一个状态就是新的『基态』
     const clone: Update<State> = {
     eventTime: updateEventTime,
     lane: updateLane,
     tag: update.tag,
     payload: update.payload,
     callback: update.callback,
     next: null
     };
     if (newLastBaseUpdate === null) {
     newFirstBaseUpdate = newLastBaseUpdate = clone;
     newBaseState = newState;
     } else {
     newLastBaseUpdate = newLastBaseUpdate.next = clone;
     }
     newLanes = mergeLanes(newLanes, updateLane);
     } else {
     // 优先级足够
     if (newLastBaseUpdate !== null) {
     // 即将被提交的 update
     const clone: Update<State> = {
     eventTime: updateEventTime,
     lane: NoLane, // 0 是所有位掩码的子集
     tag: update.tag,
     payload: update.payload,
     callback: update.callback,
     next: null
     };
     newLastBaseUpdate = newLastBaseUpdate.next = clone;
     }
     // 处理 update,计算新的 state
     newState = getStateFromUpdate(
     workInProgress,
     queue,
     update,
     newState,
     props,
     instance
     );
     const callback = update.callback;
     if (callback !== null) {
     workInProgress.flags |= Callback;
     const effects = queue.effects;
     if (effects === null) {
     queue.effects = [update];
     } else {
     effects.push(update);
     }
     }
     }
     update = update.next;
     if (update === null) {
     pendingQueue = queue.shared.pending;
     if (pendingQueue === null) {
     break;
     } else {
     const lastPendingUpdate = pendingQueue;
     const firstPendingUpdate = lastPendingUpdate.next;
     lastPendingUpdate.next = null;
     update = firstPendingUpdate;
     queue.lastBaseUpdate = lastPendingUpdate;
     queue.shared.pending = null;
     }
     }
     } while (true);
     if (newLastBaseUpdate === null) {
     newBaseState = newState;
     }
     queue.baseState = newBaseState;
     queue.firstBaseUpdate = newFirstBaseUpdate;
     queue.lastBaseUpdate = newLastBaseUpdate;
     /**
     * 将剩余过期时间设置为队列中剩余的时间
     * 影响过期时间的另外两个因素就是 props 和 context
     */
     markSkippedUpdateLanes(newLanes);
     workInProgress.lanes = newLanes;
     workInProgress.memoizedState = newState;
     }
    }
    // 最终由 getStateFromUpdate 计算出 state
    function getStateFromUpdate<State>(
     workInProgress: Fiber,
     queue: UpdateQueue<State>,
     update: Update<State>,
     prevState: State,
     nextProps: any,
     instance: any
    ): any {
     switch (update.tag) {
     case ReplaceState: {
     const payload = update.payload;
     if (typeof payload === 'function') {
     const nextState = payload.call(instance, prevState, nextProps);
     return nextState;
     }
     return payload;
     }
     case CaptureUpdate: {
     workInProgress.flags =
     (workInProgress.flags & ~ShouldCapture) | DidCapture;
     }
     case UpdateState: {
     const payload = update.payload;
     let partialState;
     if (typeof payload === 'function') {
     partialState = payload.call(instance, prevState, nextProps);
     } else {
     partialState = payload;
     }
     if (partialState === null || partialState === undefined) {
     return prevState;
     }
     return Object.assign({}, prevState, partialState);
     }
     case ForceUpdate: {
     hasForceUpdate = true;
     return prevState;
     }
     }
     return prevState;
    }
    
    • legacy 模式:

      1
      
      ReactDOM.render(<App />, rootNode);
      
      • 在同步操作中,setState 是异步的;
      • 在异步操作中,setState 是同步的;
      • 创建的任务/更新均为同样的优先级。
    • concurrent 模式:

      1
      
      ReactDOM.createRoot(rootNode).render(<App />);
      
      • 更急迫的更新可以中断已经开始的渲染;
      • 创建的任务/更新有不同的优先级;
      • 渲染过程可以被打断;
      • setState 是异步。

    生命周期

    reconciler 阶段(render 阶段);生成新的 fiber 树并收集本次要更新的点;对应的钩子为:

    • constructor
    • static getDerivedStateFromProps
    • static getDerivedStateFromError(发生错误的组件不会触发自身的钩子)
    • shouldComponentUpdate
    • render

    commit 阶段(renderer 阶段);将要更新的点 patch 到 DOM 上并切换 fiber 树;对应的钩子为:

    • getSnapshotBeforeUpdate
    • componentDidMount
    • componentDidUpdate
    • componentDidCatch(发生错误的组件不会触发自身的钩子)
    • componentWillUnmount

    为什么要对生命周期钩子大换血?

    组件在挂载过程中会调三次钩子(constructor、componentWillMount、render),组件在更新过程中会调四次钩子(componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render);总不能每个方法都用 try…catch 包起来,这样会性能很差;而 constructor、render 是不可避免的,于是对三个 willXXX 动刀了。

    在早期版本中,componentWillMount 与 componentWillReceiveProps 会做内部优化,执行多次 setState 都会延后到 render 时进行合并处理;因此用户就肆意 setState 了。这些 willXXX 还可以让用户任意操作 DOM;操作 DOM 会可能 reflow,这是官方不愿意看到的。于是官方推出了 getDerivedStateFromProps,让你在 render 设置新 state,你主要返回一个新对象,它就主动帮你 setState。由于这是一个静态方法,你不能操作 instance,这就阻止了你多次操作 setState。由于没有 instance,也就没有 instance.refs.xxx,你也没有机会操作 DOM 了。这样一来,getDerivedStateFromProps 的逻辑应该会很简单,这样就不会出错,就不会打断 DFS 过程。

    getDerivedStateFromProps 取代了原来的 componentWillMount 与 componentWillReceiveProps 方法,而 componentWillUpdate 本来就是可有可无,以前完全是为了对称好看。在即使到来的异步更新中,render 阶段可能执行多次,才执行一次 commit;这样也会导致 willXXX 钩子执行多次,违反它们的语义,它们的废弃是不可逆转的。在进入 commit 阶段时,组件多了一个新钩子叫 getSnapshotBeforeUpdate,它与 commit 阶段的钩子一样只执行一次。如果出错呢,在 componentDidMount/Update 后,我们可以使用 componentDidCatch 方法。

    render 阶段的钩子都不应该操作 DOM,最好也不要 setState,我们称之为轻量钩子;commit 阶段的钩子则对应称之为重量钩子。

    • constructor
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    /**
     * 以下情况会调用 constructClassInstance(构建类组件实例):
     * 1. updateClassComponent(如果 instance 为 null)
     * 2. mountIncompleteClassComponent
     */
    function constructClassInstance(
     workInProgress: Fiber,
     ctor: any,
     props: any
    ): any {
     // 处理上下文
     const contextType = ctor.contextType;
     let isLegacyContextConsumer = false;
     let unmaskedContext = emptyContextObject; // {}
     let context = emptyContextObject; // {}
     if (typeof contextType === 'object' && contextType !== null) {
     context = readContext(contextType);
     } else if (!disableLegacyContext) {
     unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
     const contextTypes = ctor.contextTypes;
     isLegacyContextConsumer =
     contextTypes !== null && contextTypes !== undefined;
     context = isLegacyContextConsumer
     ? getMaskedContext(workInProgress, unmaskedContext)
     : emptyContextObject;
     }
     // 创建组建实例
     const instance = new ctor(props, context);
     const state = (workInProgress.memoizedState =
     instance.state !== null && instance.state !== undefined
     ? instance.state
     : null);
     /**
     * 实例需要访问到 fiber,以便可以安排更新
     * instance.updater = classComponentUpdater
     * workInProgress.stateNode = instance
     * instance._reactInternals = workInProgress
     */
     adoptClassInstance(workInProgress, instance);
     /**
     * 缓存 unmasked context,可以避免在不必要的情况下重新创建 masked context
     * ReactFiberContext 通常更新此缓存,但不能更新新创建的实例
     */
     if (isLegacyContextConsumer) {
     cacheContext(workInProgress, unmaskedContext, context);
     }
     return instance;
    }
    
    • getDerivedStateFromProps
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    /**
     * 以下情况会调用 applyDerivedStateFromProps:
     * 1. mountClassInstance:挂载组建实例
     * 2. resumeMountClassInstance:复用组建实例
     * 3. updateClassInstance:更新组建实例
     */
    export function applyDerivedStateFromProps(
     workInProgress: Fiber,
     ctor: any,
     getDerivedStateFromProps: (props: any, state: any) => any,
     nextProps: any,
    ) {
     // 更新前/初始状态
     const prevState = workInProgress.memoizedState;
     // getDerivedStateFromProps 接受新的 props 和旧的 state,返回变化的 state
     const partialState = getDerivedStateFromProps(nextProps, prevState);
     // 合并为更新后的/挂载后的状态
     const memoizedState =
     partialState === null || partialState === undefined
     ? prevState
     : Object.assign({}, prevState, partialState);
     // 保存在 workInProgress 中
     workInProgress.memoizedState = memoizedState;
     // 更新队列空了之后,将新的状态持久化到基本状态
     if (workInProgress.lanes === NoLanes) {
     // 对于类,队列始终为非 null
     const updateQueue: UpdateQueue<any> = (workInProgress.updateQueue: any);
     updateQueue.baseState = memoizedState;
     }
    }
    
    • shouldComponentUpdate
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    /**
     * shouldUpdate 会在以下情况求值,来确定是否更新:
     * 1. resumeMountClassInstance
     * 2. updateClassInstance
     */
    const shouldUpdate =
     // this.forceUpdate 会将 hasForceUpdate 置为 true,所以组件一定会更新
     checkHasForceUpdateAfterProcessing() ||
     checkShouldComponentUpdate(
     workInProgress,
     ctor,
     oldProps,
     newProps,
     oldState,
     newState,
     nextContext
     );
    function checkShouldComponentUpdate(
     workInProgress,
     ctor,
     oldProps,
     newProps,
     oldState,
     newState,
     nextContext
    ) {
     const instance = workInProgress.stateNode;
     if (typeof instance.shouldComponentUpdate === 'function') {
     // 如果用户引入了 shouldComponentUpdate,计算组件是否应该更新
     const shouldUpdate = instance.shouldComponentUpdate(
     newProps,
     newState,
     nextContext
     );
     return shouldUpdate;
     }
     if (ctor.prototype && ctor.prototype.isPureReactComponent) {
     // 如果继承了 PureComponent,对 props 和 state 做浅比较
     return (
     !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
     );
     }
     return true;
    }
    
    • render
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    
    /**
     * 以下情况会调用 updateClassComponent:
     * 1. mountIncompleteClassComponent
     * 2. mountIndeterminateComponent
     */
    function finishClassComponent(
     current: Fiber | null,
     workInProgress: Fiber,
     Component: any,
     shouldUpdate: boolean,
     hasContext: boolean,
     renderLanes: Lanes
    ) {
     // 即使 shouldComponentUpdate 返回的是 false,refs 也得更新
     markRef(current, workInProgress);
     const didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;
     if (!shouldUpdate && !didCaptureError) {
     if (hasContext) {
     invalidateContextProvider(workInProgress, Component, false);
     }
     return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
     }
     const instance = workInProgress.stateNode;
     ReactCurrentOwner.current = workInProgress;
     let nextChildren;
     if (
     didCaptureError &&
     typeof Component.getDerivedStateFromError !== 'function'
     ) {
     /**
     * 如果捕获到错误,但是没有 getDerivedStateFromError 钩子,卸载所有子组件
     * componentDidCatch 将安排更新来重新渲染 fallback
     */
     nextChildren = null;
     } else {
     nextChildren = instance.render();
     }
     workInProgress.flags |= PerformedWork;
     if (current !== null && didCaptureError) {
     /**
     * 如果我们正在从错误中恢复,请在不重用任何现有子节点的情况下进行协调
     * 从概念上讲,正常的子节点和因错而显示的子节点是两个不同的集合
     * 即使它们的身份匹配,我们也不应该重用正常的子节点
     */
     forceUnmountCurrentAndReconcile(
     current,
     workInProgress,
     nextChildren,
     renderLanes
     );
     } else {
     reconcileChildren(current, workInProgress, nextChildren, renderLanes);
     }
     // 保存刚刚用来渲染的状态
     workInProgress.memoizedState = instance.state;
     // 重新计算可能发生改变的 context 传过来的值
     if (hasContext) {
     invalidateContextProvider(workInProgress, Component, true);
     }
     return workInProgress.child;
    }
    
    • getSnapshotBeforeUpdate
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    
    /**
     * 会在 commitBeforeMutationLifeCycles 里调用(就是在最终 commit 之前被调用)
     * 之后便会 commitMutationEffects 执行 commitWork
     */
    function commitBeforeMutationLifeCycles(
     current: Fiber | null,
     finishedWork: Fiber
    ): void {
     switch (finishedWork.tag) {
     case FunctionComponent:
     case ForwardRef:
     case SimpleMemoComponent:
     case Block: {
     return;
     }
     case ClassComponent: {
     if (finishedWork.flags & Snapshot) {
     if (current !== null) {
     const prevProps = current.memoizedProps;
     const prevState = current.memoizedState;
     const instance = finishedWork.stateNode;
     // snapshot 会作为 componentDidUpdate 的第三个参数
     const snapshot = instance.getSnapshotBeforeUpdate(
     finishedWork.elementType === finishedWork.type
     ? prevProps
     : resolveDefaultProps(finishedWork.type, prevProps),
     prevState
     );
     instance.__reactInternalSnapshotBeforeUpdate = snapshot;
     }
     }
     return;
     }
     case HostRoot: {
     if (supportsMutation) {
     if (finishedWork.flags & Snapshot) {
     const root = finishedWork.stateNode;
     clearContainer(root.containerInfo);
     }
     }
     return;
     }
     case HostComponent:
     case HostText:
     case HostPortal:
     case IncompleteClassComponent:
     return;
     }
    }
    
    • componentDidMount/componentDidUpdate
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    /**
     * 在 commitRoot 里循环调用 commitLayoutEffects 直到 nextEffect 为 null
     * commitLifeCycles 会在 commitLayoutEffects 里被调用
     */
    function commitLifeCycles(
     finishedRoot: FiberRoot,
     current: Fiber | null,
     finishedWork: Fiber,
     committedLanes: Lanes,
     ): void {
     switch (finishedWork.tag) {
     // ...
     case ClassComponent: {
     const instance = finishedWork.stateNode;
     if (finishedWork.flags & Update) {
     // 根据是否是初次渲染,选择调用 componentDidMount 还是 componentDidUpdate
     if (current === null) {
     instance.componentDidMount();
     } else {
     const prevProps =
     finishedWork.elementType === finishedWork.type
     ? current.memoizedProps
     : resolveDefaultProps(finishedWork.type, current.memoizedProps);
     const prevState = current.memoizedState;
     instance.componentDidUpdate(
     prevProps,
     prevState,
     instance.__reactInternalSnapshotBeforeUpdate,
     );
     }
     }
     const updateQueue = (finishedWork.updateQueue: any);
     if (updateQueue !== null) {
     commitUpdateQueue(finishedWork, updateQueue, instance);
     }
     return;
     }
     // ...
     }
     }
    
    • componentWillUnmount
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    /**
     * 组件卸载时会调用 commitUnmount,其中包含了不同类型的组件的卸载方式
     * 对于 ClassComponent 会先解绑 ref(safelyDetachRef)再调用 safelyCallComponentWillUnmount
     * safelyCallComponentWillUnmount 会在 try...catch 里调用 callComponentWillUnmountWithTimer
     */
    const callComponentWillUnmountWithTimer = function (current, instance) {
     instance.props = current.memoizedProps;
     instance.state = current.memoizedState;
     instance.componentWillUnmount();
    };
    
    • getDerivedStateFromError/componentDidCatch
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    
    /**
     * 在 renderRootSync 的过程中,如果 workLoopSync/workLoopConcurrent 执行出错,会执行 handleError
     * throwException 会将 createClassErrorUpdate 的返回值通过 enqueueCapturedUpdate 加入更新队列
     */
    function createClassErrorUpdate(
     fiber: Fiber,
     errorInfo: CapturedValue<mixed>,
     lane: Lane
    ): Update<mixed> {
     const update = createUpdate(NoTimestamp, lane);
     update.tag = CaptureUpdate;
     const getDerivedStateFromError = fiber.type.getDerivedStateFromError;
     if (typeof getDerivedStateFromError === 'function') {
     const error = errorInfo.value;
     update.payload = () => {
     logCapturedError(fiber, errorInfo);
     return getDerivedStateFromError(error);
     };
     }
     const inst = fiber.stateNode;
     if (inst !== null && typeof inst.componentDidCatch === 'function') {
     update.callback = function callback() {
     if (typeof getDerivedStateFromError !== 'function') {
     /**
     * 为了保留错误边界的先前存在的重试行为
     * 我们跟踪在这一批中哪些已经失败
     * 在我们返回到浏览器之前,它会被重置
     */
     markLegacyErrorBoundaryAsFailed(this);
     // log 一下,如果 componentDidCatch 是唯一被定义的错误边界方法
     logCapturedError(fiber, errorInfo);
     }
     const error = errorInfo.value;
     const stack = errorInfo.stack;
     this.componentDidCatch(error, {
     componentStack: stack !== null ? stack : ''
     });
     };
     }
     return update;
    }
    export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {
     if (sourceFiber.tag === HostRoot) {
     // 错误在根组件被抛出,没有父级,需要根组件捕获
     captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);
     return;
     }
     let fiber = sourceFiber.return;
     while (fiber !== null) {
     // 向上查找
     if (fiber.tag === HostRoot) {
     captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);
     return;
     } else if (fiber.tag === ClassComponent) {
     const ctor = fiber.type;
     const instance = fiber.stateNode;
     if (
     typeof ctor.getDerivedStateFromError === 'function' ||
     (typeof instance.componentDidCatch === 'function' &&
     !isAlreadyFailedLegacyErrorBoundary(instance))
     ) {
     const errorInfo = createCapturedValue(error, sourceFiber);
     const update = createClassErrorUpdate(fiber, errorInfo, SyncLane);
     enqueueUpdate(fiber, update);
     const eventTime = requestEventTime();
     const root = markUpdateLaneFromFiberToRoot(fiber, SyncLane);
     if (root !== null) {
     markRootUpdated(root, SyncLane, eventTime);
     ensureRootIsScheduled(root, eventTime);
     schedulePendingInteractions(root, SyncLane);
     } else {
     // 组件已经被卸载,为了错误不被吞掉,可以调用 log 边界
     if (
     typeof instance.componentDidCatch === 'function' &&
     !isAlreadyFailedLegacyErrorBoundary(instance)
     ) {
     try {
     instance.componentDidCatch(error, errorInfo);
     } catch (errorToIgnore) {
     // 边缘情况
     }
     }
     }
     return;
     }
     }
     fiber = fiber.return;
     }
    }
    

    参考

    • React 的组件类型
    • React Fiber 架构
    • React 技术揭秘


沪ICP备19023445号-2号
友情链接