1,如何解读?
初级
- 掌握react语法,了解其用法
import React from 'react'
class Square extends React.Component {
render() {
return (
<button className="square">
hello
</button>
);
}
}
const rootNode = document.getElementById('root');
// legacy模式
ReactDOM.render(<Square /> ,rootNode);
// concurrent模式
ReactDOM.createRoot(rootNode).render(<Square />);
中级
- 理解主要流程,对着 build-your-own-react 一行行调试,这个demo涵盖了主要的渲染更新过程:render、setState、useState/useReducer
高级
- 进阶看文章,推荐 文章1:react源码解析、文章2:react技术解密,
- 沿着一个入口顺藤摸瓜读到尾,带着疑问去读吧
- 首次如何渲染到页面?
reactDom.createRoot.render(<App/>)发生了什么?- Scheduler MessageChannel、lane模型?
- 响应式原理?
- 函数式组件 `this.setState mvvm原理?
- hook useState/useReducer` mvvm原理?
- 其他一些 黑科技
- useEffect和useLayoutEffect有什么区别?
- React 16中e.stopPropagation()为啥失灵?
- context 如何跨层级通讯?
- ref用法实现?
2,前置知识
2.1 jsx
安装babel-tools包,jsx目录下编写jsx代码,编译运行
npm install -g babel-tools
jsx hello/jsx/ hello/build/
转换结果如下:
function Comp() {
return <a>123</a>
}
<Comp id="div">
<span>1</span>
<span>1</span>
</Comp>
"use strict";
function Comp() {
return React.createElement("a", null, "123");
}
React.createElement(Comp,
{ id: "div" },
React.createElement("span", null, "1"),
React.createElement("span", null, "1")
);
createElement函数
export function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
// 将 config 处理后赋值给 props
// ...省略
}
const childrenLength = arguments.length - 2;
// 处理 children,会被赋值给props.children
// ...省略
// 处理 defaultProps
// ...省略
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// 标记这是个 React Element
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: ref,
props: props,
_owner: owner,
};
return element;
};
2.2 fiber
//FiberRootNode
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.current = null;
this.pingCache = null;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.callbackNode = null;
this.callbackPriority = NoLanePriority;
this.eventTimes = createLaneMap(NoLanes);
this.expirationTimes = createLaneMap(NoTimestamp);
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes);
...
}
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
}
2.3 update
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
},
effects: null,
};
fiber.updateQueue = queue;
export function createUpdate(eventTime: number, lane: Lane): Update<*> {
const update: Update<*> = {
eventTime,
lane,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
return update;
}
//queue.shared.pending.next = update
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
const updateQueue = fiber.updateQueue;
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
const pending = sharedQueue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
}
2.4 时间切片
在浏览器的一帧中js的执行时间如下
一个task(宏任务) -- 队列中全部job(微任务) -- requestAnimationFrame -- 浏览器重排/重绘 -- requestIdleCallback
requestIdleCallback是在浏览器重绘重排之后,如果还有空闲就可以执行的时机,所以为了不影响重绘重排,可以在浏览器在requestIdleCallback中执行耗性能的计算,但是由于requestIdleCallback存在兼容和触发时机不稳定的问题,scheduler中采用MessageChannel来实现requestIdleCallback,当前环境不支持MessageChannel就采用setTimeout。
2.5 带优先级的状态更新
React中有三套优先级机制:
-
- React事件优先级
runWithPriority:以一个优先级执行callback
// 离散事件优先级,例如:点击事件,input输入等触发的更新任务,优先级最高
export const DiscreteEventPriority: EventPriority = SyncLane;
// 连续事件优先级,例如:滚动事件,拖动事件等,连续触发的事件
export const ContinuousEventPriority: EventPriority = InputContinuousLane;
// 默认事件优先级,例如:setTimeout触发的更新任务
export const DefaultEventPriority: EventPriority = DefaultLane;
// 闲置事件优先级,优先级最低
export const IdleEventPriority: EventPriority = IdleLane;
-
- Lane优先级
基于 ui的优先级 > cpu优先级,提出更细颗粒的lane优先级;
lane优先级计算 由 事件优先级来决定,lanes模型类似赛车道,不同优先级占用不同位置的‘赛道’,而且存在批的概念,优先级越低,‘赛道’越多。从高优先级的lanes开始的。
Lane是React中用于表示任务的优先级。优先级分为高优先级与低优先级,当用户操作界面时,为了避免页面卡顿,需要让出线程的执行权,先执行用户触发的事件,这个我们称之为高优先级任务,其它不那么重要的事件我们称之为低优先级任务。
不同优先级的任务间,会存在一种现象:当执行低优先级任务时,突然插入一个高优先级任务,那么会中断低优先级的任务,先执行高优先级的任务,我们可以将这种现象称为任务插队。当高优先级任务执行完,准备执行低优先级任务时,又插入一个高优先级任务,那么又会执行高优先级任务,如果不断有高优先级任务插队执行,那么低优先级任务便一直得不到执行,我们称这种现象为任务饥饿问题。
// lane使用31位二进制来表示优先级车道共31条, 位数越小(1的位置越靠右)表示优先级越高
export const TotalLanes = 31;
// 没有优先级
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
// 同步优先级,表示同步的任务一次只能执行一个,例如:用户的交互事件产生的更新任务
export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
// 连续触发优先级,例如:滚动事件,拖动事件等
export const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000000100;
// 默认优先级,例如使用setTimeout,请求数据返回等造成的更新
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000000001000;
export const DefaultLane: Lanes = /* */ 0b0000000000000000000000000010000;
// 过度优先级,例如: Suspense、useTransition、useDeferredValue等拥有的优先级
const TransitionHydrationLane: Lane = /* */ 0b0000000000000000000000000100000;
const TransitionLanes: Lanes = /* */ 0b0000000001111111111111111000000;
const TransitionLane1: Lane = /* */ 0b0000000000000000000000001000000;
const TransitionLane2: Lane = /* */ 0b0000000000000000000000010000000;
const TransitionLane3: Lane = /* */ 0b0000000000000000000000100000000;
const TransitionLane4: Lane = /* */ 0b0000000000000000000001000000000;
const TransitionLane5: Lane = /* */ 0b0000000000000000000010000000000;
const TransitionLane6: Lane = /* */ 0b0000000000000000000100000000000;
const TransitionLane7: Lane = /* */ 0b0000000000000000001000000000000;
const TransitionLane8: Lane = /* */ 0b0000000000000000010000000000000;
const TransitionLane9: Lane = /* */ 0b0000000000000000100000000000000;
const TransitionLane10: Lane = /* */ 0b0000000000000001000000000000000;
const TransitionLane11: Lane = /* */ 0b0000000000000010000000000000000;
const TransitionLane12: Lane = /* */ 0b0000000000000100000000000000000;
const TransitionLane13: Lane = /* */ 0b0000000000001000000000000000000;
const TransitionLane14: Lane = /* */ 0b0000000000010000000000000000000;
const TransitionLane15: Lane = /* */ 0b0000000000100000000000000000000;
const TransitionLane16: Lane = /* */ 0b0000000001000000000000000000000;
const RetryLanes: Lanes = /* */ 0b0000111110000000000000000000000;
const RetryLane1: Lane = /* */ 0b0000000010000000000000000000000;
const RetryLane2: Lane = /* */ 0b0000000100000000000000000000000;
const RetryLane3: Lane = /* */ 0b0000001000000000000000000000000;
const RetryLane4: Lane = /* */ 0b0000010000000000000000000000000;
const RetryLane5: Lane = /* */ 0b0000100000000000000000000000000;
export const SomeRetryLane: Lane = RetryLane1;
export const SelectiveHydrationLane: Lane = /* */ 0b0001000000000000000000000000000;
-
- Scheduler优先级
scheduleCallback:以一个优先级注册callback,在适当的时机执行
export const NoPriority = 0; //没有优先级
export const ImmediatePriority = 1; // 立即执行任务的优先级,级别最高
export const UserBlockingPriority = 2; // 用户阻塞的优先级
export const NormalPriority = 3; // 正常优先级
export const LowPriority = 4; // 较低的优先级
export const IdlePriority = 5; // 优先级最低,闲表示任务可以闲置
3,解读流程
3.1 入口 ReactDOM.createRoot(rootNode).render(<App/>)
ReactDOM.createRoot(rootNode)
ReactDOM.createRoot(rootNode)实例化ReactDOMRoot
- createContainer 返回一个根虚拟dom
- listenToAllSupportedEvents 事件的注册
- markContainerAsRoot 建立虚拟dom与实际节点的关系
export function createRoot(
container: Container,
options?: RootOptions,
): RootType {
return new ReactDOMRoot(container, options);
}
function ReactDOMRoot(container: Container, options: void | RootOptions) {
this._internalRoot = createRootImpl(container, ConcurrentRoot, options);
}
function createRootImpl(
container: Container,
tag: RootTag,
options: void | RootOptions,
) {
const root = createContainer(container, tag, hydrate, hydrationCallbacks);
markContainerAsRoot(root.current, container);
const containerNodeType = container.nodeType;
listenToAllSupportedEvents(rootContainerElement);
return root;
}
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
export function markContainerAsRoot(hostRoot: Fiber, node: Container): void {
node[internalContainerInstanceKey] = hostRoot;
}
- new FiberRootNode 创建虚拟dom
- createHostRootFiber 建立current rootFiber
- initializeUpdateQueue 初始化更新队列
export function createContainer(
containerInfo: Container,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): OpaqueRoot {
return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
}
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
const uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
initializeUpdateQueue(uninitializedFiber);
return root;
}
FiberRootNode虚拟dom节点创建
//FiberRootNode
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.current = null;
this.pingCache = null;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.callbackNode = null;
this.callbackPriority = NoLanePriority;
this.eventTimes = createLaneMap(NoLanes);
this.expirationTimes = createLaneMap(NoTimestamp);
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes);
...
}
// createHostRootFiber
export function createHostRootFiber(tag: RootTag): Fiber {
let mode;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode | BlockingMode | StrictMode;
} else if (tag === BlockingRoot) {
mode = BlockingMode | StrictMode;
} else {
mode = NoMode;
}
return createFiber(HostRoot, null, null, mode);
}
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: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
}
initializeUpdateQueue
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
},
effects: null,
};
fiber.updateQueue = queue;
}
ReactDOM.createRoot(rootNode).render()
ReactDOM.createRoot(rootNode).render()即调用ReactDOMRoot.prototype.render方法,主要是调用updateContainer方法
- _internalRoot 为createContainer返回的node
- createUpdate 创建一个更新
- enqueueUpdate 将该更新插入更新队列
- scheduleUpdateOnFiber 协调fiber上的更新
ReactDOMRoot.prototype.render = ReactDOMBlockingRoot.prototype.render = function(
children: ReactNodeList,
): void {
const root = this._internalRoot;
updateContainer(children, root, null, null);
};
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
const current = container.current;
const lane = requestUpdateLane(current);
const update = createUpdate(eventTime, lane);
update.payload = {element};
enqueueUpdate(current, update);
scheduleUpdateOnFiber(current, lane, eventTime);
return lane;
}
3.2 scheduler
Scheduler主要的功能是时间切片和调度优先级,react在对比差异的时候会占用一定的js执行时间,Scheduler内部借助MessageChannel实现了在浏览器绘制之前指定一个时间片,如果react在指定时间内没对比完,Scheduler就会强制交出执行权给浏览器
scheduleUpdateOnFiber
- 若为同步更新执行 performSyncWorkOnRoot
- 异步更新执行ensureRootIsScheduled
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
) {
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
markRootUpdated(root, lane, eventTime);
const priorityLevel = getCurrentPriorityLevel();
if (lane === SyncLane) {
schedulePendingInteractions(root, lane);
performSyncWorkOnRoot(root);
} else {
...
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, lane);
}
mostRecentlyUpdatedRoot = root;
}
ensureRootIsScheduled
设置lane赛道,每次有新的更新、或者一轮lane执行完就会重新设置
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
markStarvedLanesAsExpired(root, currentTime);
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
const newCallbackPriority = returnNextLanesPriority();
if (root.callbackNode !== null){
if(newCallbackPriority === root.callbackPriority){ return }
cancelCallback(existingCallbackNode);
}
// Schedule a new callback.
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;
}
scheduleCallback
分析上面异步过程
- timerQueue:所有没有过期的任务会放在这个队列中。
- taskQueue:所有过期的任务会放在该队列中,并且按过期时间排序,过期时间越小则排在越前面,并且越先执行
- requestHostTimeout 后构造一个定时器,到过期时间将 timerQueue 放入taskQueue来执行
export function scheduleCallback(
reactPriorityLevel: ReactPriorityLevel,
callback: SchedulerCallback,
options: SchedulerCallbackOptions | void | null,
) {
const priorityLevel = reactPriorityToSchedulerPriority(reactPriorityLevel);
return Scheduler_scheduleCallback(priorityLevel, callback, options);
}
function unstable_scheduleCallback(priorityLevel, callback, options) {
var currentTime = getCurrentTime();
var startTime = options.delay || currentTime
var timeout;
switch (priorityLevel) {
case ImmediatePriority:
timeout = IMMEDIATE_PRIORITY_TIMEOUT;
break;
case UserBlockingPriority:
timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
break;
case IdlePriority:
timeout = IDLE_PRIORITY_TIMEOUT;
break;
case LowPriority:
timeout = LOW_PRIORITY_TIMEOUT;
break;
case NormalPriority:
default:
timeout = NORMAL_PRIORITY_TIMEOUT;
break;
}
var expirationTime = startTime + timeout;
var newTask = {
id: taskIdCounter++,
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: -1,
};
if (startTime > currentTime) {
newTask.sortIndex = startTime;
push(timerQueue, newTask);
if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
requestHostTimeout(handleTimeout, startTime - currentTime);
}
} else {
newTask.sortIndex = expirationTime;
push(taskQueue, newTask);
if (!isHostCallbackScheduled && !isPerformingWork) {
isHostCallbackScheduled = true;
requestHostCallback(flushWork);
}
}
return newTask;
}
requestHostCallback
第一次执行isMessageLoopRunning=false,执行 port.postMessage(null) 触发 channel.port1.onmessage,即performWorkUntilDeadline函数
- flushWork函数会依次执行taskQueue中的任务
- performWorkUntilDeadline在下一个微任务中继续执行scheduledHostCallback,来执行flushWork函数
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;
//callback = flushWork
requestHostCallback = function(callback) {
scheduledHostCallback = callback;
if (!isMessageLoopRunning) {
isMessageLoopRunning = true;
port.postMessage(null);
}
};
function flushWork(hasTimeRemaining, initialTime) {
...
isHostCallbackScheduled = false
return workLoop(hasTimeRemaining, initialTime);
}
function workLoop(hasTimeRemaining, initialTime) {
let currentTime = initialTime;
advanceTimers(currentTime);
currentTask = peek(taskQueue);
while (
currentTask !== null &&
!(enableSchedulerDebugging && isSchedulerPaused)
) {
const callback = currentTask.callback;
if (typeof callback === 'function') {
// 对应callbak即performConcurrentWorkOnRoot返回的继续执行
const continuationCallback = callback(didUserCallbackTimeout);
currentTask.callback = continuationCallback
...
}
currentTask = peek(taskQueue);
}
//当前任务被中断,scheduler会继续发起调度,执行任务
if (currentTask !== null) {
return true;
} else {
//currentTask为null,则表示taskQueue中的任务都执行完成了
//则判断timerQueue中是否有任务,有任务的话会去检查是否有过期任务
//有的话则添加到taskQueue中,并重新发起调度执行任务
const firstTimer = peek(timerQueue);
if (firstTimer !== null) {
requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
}
return false;
}
}
performWorkUntilDeadline
- 第一次进来执行port.postMessage方法,触发 performWorkUntilDeadline 方法
const performWorkUntilDeadline = () => {
if (scheduledHostCallback !== null) {
const currentTime = getCurrentTime();
deadline = currentTime + yieldInterval;
const hasTimeRemaining = true;
const hasMoreWork = scheduledHostCallback(
hasTimeRemaining,
currentTime,
);
if (!hasMoreWork) {
isMessageLoopRunning = false;
scheduledHostCallback = null;
} else {
port.postMessage(null);
}
} else {
isMessageLoopRunning = false;
}
needsPaint = false;
};
3.4 lane模型
获取lane
requestUpdateLane
在调度前会先获取lane,会根据getCurrentPriorityLevel()的优先级得到当前lane优先级。
- getCurrentPriorityLevel()返回currentPriorityLevel,这个由事件的优先级决定
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
const current = container.current;
const lane = requestUpdateLane(current);
const update = createUpdate(eventTime, lane);
update.payload = {element};
enqueueUpdate(current, update);
scheduleUpdateOnFiber(current, lane, eventTime);
return lane;
}
export function requestUpdateLane(fiber: Fiber): Lane {
const mode = fiber.mode;
if ((mode & BlockingMode) === NoMode) {
return (SyncLane: Lane);
} else if ((mode & ConcurrentMode) === NoMode) {
return getCurrentPriorityLevel() === ImmediateSchedulerPriority
? (SyncLane: Lane)
: (SyncBatchedLane: Lane);
} else if (
!deferRenderPhaseUpdateToNextBatch &&
(executionContext & RenderContext) !== NoContext &&
workInProgressRootRenderLanes !== NoLanes
) {
return pickArbitraryLane(workInProgressRootRenderLanes);
}
}
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
) {
// 1
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
// 2
markRootUpdated(root, lane, eventTime);
const priorityLevel = getCurrentPriorityLevel();
if (lane === SyncLane) {
schedulePendingInteractions(root, lane);
performSyncWorkOnRoot(root);
} else {
...
// 3
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, lane);
}
mostRecentlyUpdatedRoot = root;
}
markUpdateLaneFromFiberToRoot
从当前fiber往上直到rootfiber打上lanes标签,并将子fiber的lanes和父fiber的lanes合并为childLanes挂在父fiber上
function markUpdateLaneFromFiberToRoot(
sourceFiber: Fiber,
lane: Lane,
): FiberRoot | null {
// Update the source fiber's lanes
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
let alternate = sourceFiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}
// Walk the parent path to the root and update the child expiration time.
let node = sourceFiber;
let parent = sourceFiber.return;
while (parent !== null) {
parent.childLanes = mergeLanes(parent.childLanes, lane);
alternate = parent.alternate;
if (alternate !== null) {
alternate.childLanes = mergeLanes(alternate.childLanes, lane);
}
node = parent;
parent = parent.return;
}
if (node.tag === HostRoot) {
const root: FiberRoot = node.stateNode;
return root;
} else {
return null;
}
}
markRootUpdated
将lanes挂到 root.pendingLanes 上,用于后续的计算
export function markRootUpdated(
root: FiberRoot,
updateLane: Lane,
eventTime: number,
) {
root.pendingLanes |= updateLane;
const higherPriorityLanes = updateLane - 1; // Turns 0b1000 into 0b0111
root.suspendedLanes &= higherPriorityLanes;
root.pingedLanes &= higherPriorityLanes;
const eventTimes = root.eventTimes;
const index = laneToIndex(updateLane);
eventTimes[index] = eventTime;
}
ensureRootIsScheduled
在进行调度更新时,进行了一系列lanes模型的处理。 设置lane赛道,如存在低优先级的,则插队,进行一轮scheduleCallback操作;
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
// 饥饿避免
markStarvedLanesAsExpired(root, currentTime);
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
const newCallbackPriority = returnNextLanesPriority();
// 插队
if (nextLanes === NoLanes || root.callbackNode !== null) {
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;
}
避免饥饿 markStarvedLanesAsExpired
循环处理 root.pendingLanes ,为当前任务根据优先级添加过期时间,并检查未执行的任务中是否有任务过期,有任务过期则在expiredLanes中添加该任务的lane,在后续该任务的执行中以同步模式执行,避免饥饿问题
export function markStarvedLanesAsExpired(
root: FiberRoot,
currentTime: number,
): void {
const pendingLanes = root.pendingLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
const expirationTimes = root.expirationTimes;
let lanes = pendingLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
const expirationTime = expirationTimes[index];
if (expirationTime === NoTimestamp) {
if (
(lane & suspendedLanes) === NoLanes ||
(lane & pingedLanes) !== NoLanes
) {
expirationTimes[index] = computeExpirationTime(lane, currentTime);
}
} else if (expirationTime <= currentTime) {
root.expiredLanes |= lane;
}
lanes &= ~lane;
}
}
插队 cancelCallback
在ensureRootIsScheduled 执行getNextLanes()方法获取nextLanes,比较nextLanes与当前的优先级
- 与现有的任务优先级一样,那么则会中断当前新任务向下的执行,重用之前现有的任务
- 新任务的优先级大于现有的任务优先级,那么则会取消现有的任务的执行,优先执行优先级高的任务,实现高优先级任务插队
调度 schedulerPriorityLevel
switch (lanesToEventPriority(nextLanes))来确定schedulerPriorityLevel,在scheduleCallback函数中,根据优先级确定 timeout过期时间,来构造timerQueue与 taskQueue,scheduler会循环执行taskQueue中任务
- timerQueue:所有没有过期的任务会放在这个队列中。
- taskQueue:所有过期的任务会放在该队列中,并且按过期时间排序,过期时间越小则排在越前面,并且越先执行
跳过低优先级
在一次更新中,会跳过低优先级lane的fiber更新,主要是在begainWork和更新队列时
begainWork
在begainWork中,如果当前更新不在此轮 renderLanes 中则跳过
if (!includesSomeLane(renderLanes, updateLanes)) {
workInProgress.lanes = current.lanes;
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderLanes,
);
}
processUpdateQueue
在处理queue时会跳过低优先级,并且记录跳过的lanes,在SuspenseComponent中显示pending的效果
if (!isSubsetOfLanes(renderLanes, updateLane)) {
...
markSkippedUpdateLanes(updateLane);
}
export function markSkippedUpdateLanes(lane: Lane | Lanes): void {
workInProgressRootSkippedLanes = mergeLanes(
lane,
workInProgressRootSkippedLanes,
);
}
markRootFinished 继续下一轮的更新
markRootFinished(root, remainingLanes)将剩余的lanes放入 root.pendingLanes, 最后再次执行 ensureRootIsScheduled
// commitRoot -> commitRootImpl->markRootFinished
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
markRootFinished(root, remainingLanes);
...
ensureRootIsScheduled(root, now())
export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) {
const noLongerPendingLanes = root.pendingLanes & ~remainingLanes;
root.pendingLanes = remainingLanes;
// Let's try everything again
root.suspendedLanes = 0;
root.pingedLanes = 0;
root.expiredLanes &= remainingLanes;
root.mutableReadLanes &= remainingLanes;
root.entangledLanes &= remainingLanes;
const entanglements = root.entanglements;
const eventTimes = root.eventTimes;
const expirationTimes = root.expirationTimes;
// Clear the lanes that no longer have pending work
let lanes = noLongerPendingLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
entanglements[index] = NoLanes;
eventTimes[index] = NoTimestamp;
expirationTimes[index] = NoTimestamp;
lanes &= ~lane;
}
}
3.5 执行task,渲染 render commit
上面执行scheduleCallback函数传入performConcurrentWorkOnRoot函数,就是将performConcurrentWorkOnRoot作为一个task放入taskQueue
performConcurrentWorkOnRoot
在既定lane赛道运行,每次中断后,分三种情况执行
- renderRootConcurrent -> workLoopConcurrent -> performUnitOfWork
- finishConcurrentRender -> commitRoot(root)
function performConcurrentWorkOnRoot(root) {
let lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
let exitStatus = renderRootConcurrent(root, lanes);
if (
includesSomeLane(
workInProgressRootIncludedLanes,
workInProgressRootUpdatedLanes,
)
) {
//情景1-有新的更新则重新开始
prepareFreshStack(root, NoLanes);
} else if (exitStatus !== RootIncomplete) {
const finishedWork: Fiber = (root.current.alternate: any);
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
//情景2-commitRoot
finishConcurrentRender(root, exitStatus, lanes);
}
ensureRootIsScheduled(root, now());
if (root.callbackNode === originalCallbackNode) {
// 情景3-继续执行
return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
}
function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
do {
workLoopConcurrent();
break;
} while (true);
if (workInProgress !== null) {
return RootIncomplete;
} else {
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
return workInProgressRootExitStatus;
}
}
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
performUnitOfWork
workInProgress fiber和会和已经创建的Fiber连接起来形成Fiber树。这个过程类似深度优先遍历
- beginWork 捕获阶段
- completeUnitOfWork 冒泡阶段
function performUnitOfWork(unitOfWork: Fiber): void {
const current = unitOfWork.alternate;
let next = beginWork(current, unitOfWork,
if (next === null) {
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
ReactCurrentOwner.current = null;
}
beginWork
捕获阶段 从根节点rootFiber开始,遍历到叶子节点,每次遍历到的节点都会执行beginWork,并且传入当前Fiber节点,然后创建或复用它的子Fiber节点,并赋值给workInProgress.child
- 更新时 可复用则执行
bailoutOnAlreadyFinishedWork
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const updateLanes = workInProgress.lanes;
//1.更新阶段
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
workInProgress.type !== current.type
) {
didReceiveUpdate = true;
} else if (!includesSomeLane(renderLanes, updateLanes)) {
didReceiveUpdate = false;
switch (workInProgress.tag) {
case HostRoot:
pushHostRootContext(workInProgress);
resetHydrationState();
break;
case HostComponent:
pushHostContext(workInProgress);
break;
case ClassComponent: {
const Component = workInProgress.type;
if (isLegacyContextProvider(Component)) {
pushLegacyContextProvider(workInProgress);
}
break;
}
case ContextProvider: {
const newValue = workInProgress.memoizedProps.value;
pushProvider(workInProgress, newValue);
break;
}
...
}
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
} else {
didReceiveUpdate = false;
}
workInProgress.lanes = NoLanes;
// 2.根据tag来创建不同的fiber 最后进入reconcileChildren函数
switch (workInProgress.tag) {
case IndeterminateComponent: {
return mountIndeterminateComponent(
current,
workInProgress,
workInProgress.type,
renderLanes,
);
}
case LazyComponent: {
const elementType = workInProgress.elementType;
return mountLazyComponent(
current,
workInProgress,
elementType,
updateLanes,
renderLanes,
);
}
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case HostRoot:
return updateHostRoot(current, workInProgress, renderLanes);
case HostComponent:
return updateHostComponent(current, workInProgress, renderLanes);
case HostText:
return updateHostText(current, workInProgress);
case SuspenseComponent:
return updateSuspenseComponent(current, workInProgress, renderLanes);
case HostPortal:
return updatePortalComponent(current, workInProgress, renderLanes);
case ForwardRef: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === type
? unresolvedProps
: resolveDefaultProps(type, unresolvedProps);
return updateForwardRef(
current,
workInProgress,
type,
resolvedProps,
renderLanes,
);
}
case Fragment:
return updateFragment(current, workInProgress, renderLanes);
case Mode:
return updateMode(current, workInProgress, renderLanes);
case Profiler:
return updateProfiler(current, workInProgress, renderLanes);
case ContextProvider:
return updateContextProvider(current, workInProgress, renderLanes);
case ContextConsumer:
return updateContextConsumer(current, workInProgress, renderLanes);
case MemoComponent: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
return updateMemoComponent(
current,
workInProgress,
type,
resolvedProps,
updateLanes,
renderLanes,
);
}
case SimpleMemoComponent: {
return updateSimpleMemoComponent(
current,
workInProgress,
workInProgress.type,
workInProgress.pendingProps,
updateLanes,
renderLanes,
);
}
case IncompleteClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return mountIncompleteClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case SuspenseListComponent: {
return updateSuspenseListComponent(current, workInProgress, renderLanes);
}
case FundamentalComponent: {
if (enableFundamentalAPI) {
return updateFundamentalComponent(current, workInProgress, renderLanes);
}
break;
}
case ScopeComponent: {
if (enableScopeAPI) {
return updateScopeComponent(current, workInProgress, renderLanes);
}
break;
}
case Block: {
if (enableBlocksAPI) {
const block = workInProgress.type;
const props = workInProgress.pendingProps;
return updateBlock(current, workInProgress, block, props, renderLanes);
}
break;
}
case OffscreenComponent: {
return updateOffscreenComponent(current, workInProgress, renderLanes);
}
case LegacyHiddenComponent: {
return updateLegacyHiddenComponent(current, workInProgress, renderLanes);
}
}
}
reconcileChildren/mountChildFibers
创建子fiber的过程会进入reconcileChildren,该函数的作用是为workInProgress fiber节点生成它的child fiber即 workInProgress.child。然后继续深度优先遍历它的子节点执行相同的操作。
//ReactFiberBeginWork.old.js
export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderLanes: Lanes
) {
if (current === null) {
//mount时
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes,
);
} else {
//update
workInProgress.child = reconcileChildFibers(
workInProgress,
current.child,
nextChildren,
renderLanes,
);
}
}
completeUnitOfWork
- 冒泡阶段 在捕获阶段遍历到子节点之后,会执行completeWork方法,执行完成之后会判断此节点的兄弟节点存不存在,如果存在就会为兄弟节点执行completeWork,当全部兄弟节点执行完之后,会向上‘冒泡’到父节点执行completeWork,直到rootFiber。
function completeUnitOfWork(unitOfWork: Fiber): void {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
if ((completedWork.flags & Incomplete) === NoFlags) {
let 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;
}
}
completeWork
根据workInProgress.tag进入不同函数,我们以HostComponent举例
- update时(除了判断current===null外还需要判断workInProgress.stateNode===null),调用updateHostComponent处理props(包括onClick、style、children ...),并将处理好的props赋值给updatePayload,最后会保存在workInProgress.updateQueue上
- mount时 调用createInstance创建dom,将后代dom节点插入刚创建的dom中,调用finalizeInitialChildren处理props(和updateHostComponent处理的逻辑类似)
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case ContextConsumer:
case MemoComponent:
bubbleProperties(workInProgress);
return null;
case ClassComponent: {
const Component = workInProgress.type;
if (isLegacyContextProvider(Component)) {
popLegacyContext(workInProgress);
}
bubbleProperties(workInProgress);
return null;
}
case HostComponent: {
popHostContext(workInProgress);
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
} else {
const currentHostContext = getHostContext();
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
appendAllChildren(instance, workInProgress, false, false);
workInProgress.stateNode = instance;
if (workInProgress.ref !== null) {
markRef(workInProgress);
}
}
bubbleProperties(workInProgress);
return null;
}
...
}
}
function bubbleProperties(completedWork: Fiber) {
...
let child = completedWork.child;
while (child !== null) {
newChildLanes = mergeLanes(
newChildLanes,
mergeLanes(child.lanes, child.childLanes),
);
subtreeFlags |= child.subtreeFlags;
subtreeFlags |= child.flags;
child = child.sibling;
}
}
commitRoot
- prepareForCommit
- 调用flushPassiveEffects执行完所有effect的任务
- 初始化相关变量
- 赋值firstEffect给后面遍历effectList用
- commitBeforeMutationEffects
- 执行getSnapshotBeforeUpdate
- 调度useEffect
- commitMutationEffects
- 调用commitDetachRef解绑ref(第11章hook会讲解)
- 根据effectTag执行对应的dom操作
- useLayoutEffect销毁函数在UpdateTag时执行
- recursivelyCommitLayoutEffects
- 调用commitLayoutEffectOnFiber执行相关生命周期函数或者hook相关callback
- 执行commitAttachRef为ref赋值
function commitRoot(root) {
const renderPriorityLevel = getCurrentPriorityLevel();
runWithPriority(
ImmediateSchedulerPriority,
commitRootImpl.bind(null, root, renderPriorityLevel),
);
return null;
}
function commitRootImpl(root, renderPriorityLevel) {
do {
flushPassiveEffects();
} while (rootWithPendingPassiveEffects !== null);
flushRenderPhaseStrictModeWarningsInDEV();
const finishedWork = root.finishedWork;
const lanes = root.finishedLanes;
root.finishedWork = null;
root.finishedLanes = NoLanes;
root.callbackNode = null;
let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
markRootFinished(root, remainingLanes);
if (root === workInProgressRoot) {
workInProgressRoot = null;
workInProgress = null;
workInProgressRootRenderLanes = NoLanes;
}
const subtreeHasEffects =
(finishedWork.subtreeFlags &
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
NoFlags;
const rootHasEffect =
(finishedWork.flags &
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
NoFlags;
if (subtreeHasEffects || rootHasEffect) {
let previousLanePriority;
if (decoupleUpdatePriorityFromScheduler) {
previousLanePriority = getCurrentUpdateLanePriority();
setCurrentUpdateLanePriority(SyncLanePriority);
}
const prevExecutionContext = executionContext;
executionContext |= CommitContext;
ReactCurrentOwner.current = null;
focusedInstanceHandle = prepareForCommit(root.containerInfo);
shouldFireAfterActiveInstanceBlur = false;
commitBeforeMutationEffects(finishedWork);
focusedInstanceHandle = null;
commitMutationEffects(finishedWork, root, renderPriorityLevel);
resetAfterCommit(root.containerInfo);
root.current = finishedWork;
recursivelyCommitLayoutEffects(finishedWork, root);
if (
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
(finishedWork.flags & PassiveMask) !== NoFlags
) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
scheduleCallback(NormalSchedulerPriority, () => {
flushPassiveEffects();
return null;
});
}
}
requestPaint();
executionContext = prevExecutionContext;
} else {
root.current = finishedWork;
}
remainingLanes = root.pendingLanes;
ensureRootIsScheduled(root, now());
flushSyncCallbackQueue();
return null;
}
3.5 Component beginWork
beginWork 中 会根据 workInProgress.tag 来渲染不同标签,主要分为原生dom标签和组件标签。 组件标签分为Component组件和函数组件,它们的处理方式不同。
定义
function Component(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
updateClassComponent
- 根据instance判断是否初次渲染,下面分别讨论
- nextUnitOfWork = finishClassComponent深度遍历执行reconcileChildren函数,最后得到下一个fiber
function updateClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
nextProps: any,
renderLanes: Lanes,
) {
prepareToReadContext(workInProgress, renderLanes);
const instance = workInProgress.stateNode;
let shouldUpdate;
if (instance === null) {
if (current !== null) {
current.alternate = null;
workInProgress.alternate = null;
workInProgress.flags |= Placement;
}
constructClassInstance(workInProgress, Component, nextProps);
mountClassInstance(workInProgress, Component, nextProps, renderLanes);
shouldUpdate = true;
} else if (current === null) {
// In a resume, we'll already have an instance we can reuse.
shouldUpdate = resumeMountClassInstance(
workInProgress,
Component,
nextProps,
renderLanes,
);
} else {
shouldUpdate = updateClassInstance(
current,
workInProgress,
Component,
nextProps,
renderLanes,
);
}
const nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderLanes,
);
return nextUnitOfWork;
}
function finishClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
shouldUpdate: boolean,
hasContext: boolean,
renderLanes: Lanes,
) {
// 子fiber没改动,可以复用
if (!shouldUpdate && !didCaptureError) {
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
nextChildren = instance.render();
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
初次渲染
- constructClassInstance(workInProgress, Component, nextProps) 初始化calss的实例,并且定义了instance.updater
- mountClassInstance 处理了UpdateQueue
function constructClassInstance(
workInProgress: Fiber,
ctor: any,
props: any,
): any {
const contextType = ctor.contextType;
context = readContext((contextType: any));
const instance = new ctor(props, context);
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
: null);
adoptClassInstance(workInProgress, instance);
return instance;
}
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
instance.updater = classComponentUpdater;
workInProgress.stateNode = instance;
}
const classComponentUpdater = {
isMounted,
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleUpdateOnFiber(fiber, lane, eventTime);
},
enqueueReplaceState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.tag = ReplaceState;
update.payload = payload;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleUpdateOnFiber(fiber, lane, eventTime);
},
enqueueForceUpdate(inst, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.tag = ForceUpdate;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleUpdateOnFiber(fiber, lane, eventTime);
},
};
mountClassInstance(workInProgress, Component, nextProps, renderLanes);
- UpdateQueue 初始化并且处理更新队列
- getDerivedStateFromProps 生命周期处理
- callComponentWillMount 调用ComponentWillMount生命周期
function mountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): void {
const instance = workInProgress.stateNode;
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = emptyRefsObject;
initializeUpdateQueue(workInProgress);
const contextType = ctor.contextType;
instance.context = readContext(contextType);
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
instance.state = workInProgress.memoizedState;
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
instance.state = workInProgress.memoizedState;
}
if (
typeof ctor.getDerivedStateFromProps !== 'function' &&
typeof instance.getSnapshotBeforeUpdate !== 'function' &&
(typeof instance.UNSAFE_componentWillMount === 'function' ||
typeof instance.componentWillMount === 'function')
) {
callComponentWillMount(workInProgress, instance);
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
instance.state = workInProgress.memoizedState;
}
}
更新 setState
在Component定义中可知执行 this.updater.enqueueSetState,这个过程和初次渲染render的过程差不多
- getInstance 根据组件实例获取相应fiber
- requestUpdateLane获取Lane
- createUpdate创建一个Update
- enqueueUpdate将Update插入更新队列
- scheduleUpdateOnFiber触发更新
const classComponentUpdater = {
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleUpdateOnFiber(fiber, lane, eventTime);
}
...
}
3.6 函数式组件 beginWork
hooks
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
const HooksDispatcherOnMount: Dispatcher = {
readContext,
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
useDebugValue: mountDebugValue,
useDeferredValue: mountDeferredValue,
useTransition: mountTransition,
useMutableSource: mountMutableSource,
useOpaqueIdentifier: mountOpaqueIdentifier,
unstable_isNewReconciler: enableNewReconciler,
};
const HooksDispatcherOnUpdate: Dispatcher = {
readContext,
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef,
useState: updateState,
useDebugValue: updateDebugValue,
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
useMutableSource: updateMutableSource,
useOpaqueIdentifier: updateOpaqueIdentifier,
unstable_isNewReconciler: enableNewReconciler,
};
解析 updateFunctionComponent
- renderWithHooks来获取子fiber,根据初始渲染和更新两个阶段,ReactCurrentDispatcher.current取不同值
- 深度遍历调用reconcileChildren
function updateFunctionComponent(
current,
workInProgress,
Component,
nextProps: any,
renderLanes,
) {
let nextChildren;
prepareToReadContext(workInProgress, renderLanes);
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
);
if (current !== null && !didReceiveUpdate) {
bailoutHooks(current, workInProgress, renderLanes);
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
reconcileChildren(current, workInProgress, nextChildren, 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 {
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
let children = Component(props, secondArg); //Component为函数组件,return jsx组件
...
currentHook = null;
workInProgressHook = null;
didScheduleRenderPhaseUpdate = false;
return children;
}
useState/useReducer 渲染
由hook 定义mountState中,首先执行mountWorkInProgressHook创建hooks链表workInProgressHook
// 首次渲染
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,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
由hook 定义updateReducer中,首先执行updateWorkInProgressHook获取fiber的hook
// 更新
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: any);
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) {
// We have a queue to process.
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: any),
};
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: any),
};
newBaseQueueLast = newBaseQueueLast.next = clone;
}
// Process this update.
if (update.eagerReducer === reducer) {e.
newState = ((update.eagerState: any): S);
} 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: any);
}
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
const dispatch: Dispatch<A> = (queue.dispatch: any);
return [hook.memoizedState, dispatch];
}
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;
}
useState/useReducer更新 dispatch
可以看出最终也是调用 scheduleUpdateOnFiber
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: any),
};
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;
scheduleUpdateOnFiber(fiber, lane, eventTime);
}
useEffect
- hook.memoizedState = pushEffect(...)维持memoizedState链表
//首次
function mountEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
return mountEffectImpl(
PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps,
);
}
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,
);
}
//更新
function updateEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
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,
);
}
- 在commit阶段flushPassiveEffect函数中执行memoizedState链表的函数
export function flushPassiveEffects(): boolean {
// Returns whether passive effects were flushed.
if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {
const priorityLevel =
pendingPassiveEffectsRenderPriority > NormalSchedulerPriority
? NormalSchedulerPriority
: pendingPassiveEffectsRenderPriority;
pendingPassiveEffectsRenderPriority = NoSchedulerPriority;
if (decoupleUpdatePriorityFromScheduler) {
const previousLanePriority = getCurrentUpdateLanePriority();
try {
setCurrentUpdateLanePriority(
schedulerPriorityToLanePriority(priorityLevel),
);
return runWithPriority(priorityLevel, flushPassiveEffectsImpl);
} finally {
setCurrentUpdateLanePriority(previousLanePriority);
}
} else {
return runWithPriority(priorityLevel, flushPassiveEffectsImpl);
}
}
return false;
}
function flushPassiveEffectsImpl() {
flushPassiveUnmountEffects(root.current);
flushPassiveMountEffects(root, root.current);
flushSyncCallbackQueue();
return true;
}
function flushPassiveMountEffects(root, firstChild: Fiber): void {
let fiber = firstChild;
while (fiber !== null) {
if (fiber.child !== null && primarySubtreeFlags !== NoFlags) {
flushPassiveMountEffects(root, fiber.child);
}
if ((fiber.flags & Passive) !== NoFlags) {
commitPassiveMountOnFiber(root, fiber);
}
fiber = fiber.sibling;
}
}
function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
if ((effect.tag & flags) === flags) {
// Mount
const create = effect.create;
effect.destroy = create();
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
3.7 lifecycle
3.8 event
注册
SimpleEventPlugin.registerEvents();
function registerTwoPhaseEvent(registrationName, dependencies) {
registerDirectEvent(registrationName, dependencies);
registerDirectEvent(registrationName + 'Capture', dependencies);
}
function registerDirectEvent(registrationName, dependencies) {
//...
for (var i = 0; i < dependencies.length; i++) {
allNativeEvents.add(dependencies[i]);//生成allNativeEvents对象
}
}
绑定
//由函数createRootImpl调用,也就是在创建根节点之后执行
function listenToAllSupportedEvents(rootContainerElement) {
allNativeEvents.forEach(function (domEventName) {
if (!nonDelegatedEvents.has(domEventName)) {
listenToNativeEvent(domEventName, false, rootContainerElement, null);
}
listenToNativeEvent(domEventName, true, rootContainerElement, null);
});
}
}
export function listenToNativeEvent(
domEventName: DOMEventName,
isCapturePhaseListener: boolean,
rootContainerElement: EventTarget,
targetElement: Element | null,
eventSystemFlags?: EventSystemFlags = 0,
): void {
let target = rootContainerElement;
const listenerSet = getEventListenerSet(target);
addTrappedEventListener(
target,
domEventName,
eventSystemFlags,
isCapturePhaseListener,
);
listenerSet.add(listenerSetKey);
}
function addTrappedEventListener(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
isCapturePhaseListener: boolean,
isDeferredListenerForLegacyFBSupport?: boolean,
) {
let listener = createEventListenerWrapperWithPriority(
targetContainer,
domEventName,
eventSystemFlags,
);
...
unsubscribeListener = addEventBubbleListener(
targetContainer,
domEventName,
listener,
);
}
export function createEventListenerWrapperWithPriority(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
): Function {
const eventPriority = getEventPriorityForPluginSystem(domEventName);
let listenerWrapper;
switch (eventPriority) {
case DiscreteEvent:
listenerWrapper = dispatchDiscreteEvent;
break;
case UserBlockingEvent:
listenerWrapper = dispatchUserBlockingUpdate;
break;
case ContinuousEvent:
default:
listenerWrapper = dispatchEvent;
break;
}
return listenerWrapper.bind(
null,
domEventName,
eventSystemFlags,
targetContainer,
);
}
function addEventBubbleListener(target, eventType, listener){
target.addEventListener(eventType, listener, false)
return listener;
}
触发
function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
{
flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
}
discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent);
}
export function dispatchEvent(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
nativeEvent: AnyNativeEvent,
): void {
...
dispatchEventForPluginEventSystem(
domEventName,
eventSystemFlags,
nativeEvent,
null,
targetContainer,
);
}
export function dispatchEventForPluginEventSystem(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
targetContainer: EventTarget,
): void {
let ancestorInst = targetInst;
if (
(eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0 &&
(eventSystemFlags & IS_NON_DELEGATED) === 0
) {
const targetContainerNode = ((targetContainer: any): Node);
if (targetInst !== null) {
let node = targetInst;
mainLoop: while (true) {
if (node === null) {
return;
}
const nodeTag = node.tag;
if (nodeTag === HostRoot || nodeTag === HostPortal) {
let container = node.stateNode.containerInfo;
if (isMatchingRootContainer(container, targetContainerNode)) {
break;
}
while (container !== null) {
const parentNode = getClosestInstanceFromNode(container);
if (parentNode === null) {
return;
}
const parentTag = parentNode.tag;
if (parentTag === HostComponent || parentTag === HostText) {
node = ancestorInst = parentNode;
continue mainLoop;
}
container = container.parentNode;
}
}
node = node.return;
}
}
batchedEventUpdates(() =>
dispatchEventsForPlugins(
domEventName,
eventSystemFlags,
nativeEvent,
ancestorInst,
targetContainer,
),
);
}
function dispatchEventsForPlugins(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
targetContainer: EventTarget,
): void {
const nativeEventTarget = getEventTarget(nativeEvent);
const dispatchQueue: DispatchQueue = [];
extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
processDispatchQueue(dispatchQueue, eventSystemFlags);
}
extractEvents
accumulateSinglePhaseListeners 冒泡收集事件到dispatchQueue
function extractEvents(
dispatchQueue: DispatchQueue,
domEventName: DOMEventName,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
): void {
const reactName = topLevelEventsToReactNames.get(domEventName);
let SyntheticEventCtor = SyntheticEvent;
let reactEventType: string = domEventName;
switch (domEventName) {
case 'xxx':
SyntheticEventCtor = SyntheticFocusEvent;
break;
...
}
const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
if (
enableCreateEventHandleAPI &&
eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE
) {
const listeners = accumulateEventHandleNonManagedNodeListeners(
((reactEventType: any): DOMEventName),
targetContainer,
inCapturePhase,
);
if (listeners.length > 0) {
// Intentionally create event lazily.
const event = new SyntheticEventCtor(
reactName,
reactEventType,
null,
nativeEvent,
nativeEventTarget,
);
dispatchQueue.push({event, listeners});
}
} else {
const listeners = accumulateSinglePhaseListeners(
targetInst,
reactName,
nativeEvent.type,
inCapturePhase,
accumulateTargetOnly,
);
if (listeners.length > 0) {
const event = new SyntheticEventCtor(
reactName,
reactEventType,
null,
nativeEvent,
nativeEventTarget,
);
dispatchQueue.push({event, listeners});
}
}
}
export function accumulateSinglePhaseListeners(
targetFiber: Fiber | null,
reactName: string | null,
nativeEventType: string,
inCapturePhase: boolean,
accumulateTargetOnly: boolean,
): Array<DispatchListener> {
const captureName = reactName !== null ? reactName + 'Capture' : null;
const reactEventName = inCapturePhase ? captureName : reactName;
const listeners: Array<DispatchListener> = [];
let instance = targetFiber;
let lastHostComponent = null;
// Accumulate all instances and listeners via the target -> root path.
while (instance !== null) {
const {stateNode, tag} = instance;
// Handle listeners that are on HostComponents (i.e. <div>)
if (tag === HostComponent && stateNode !== null) {
lastHostComponent = stateNode;
// createEventHandle listeners
if (enableCreateEventHandleAPI) {
const eventHandlerListeners = getEventHandlerListeners(
lastHostComponent,
);
if (eventHandlerListeners !== null) {
eventHandlerListeners.forEach(entry => {
if (
entry.type === nativeEventType &&
entry.capture === inCapturePhase
) {
listeners.push(
createDispatchListener(
instance,
entry.callback,
(lastHostComponent: any),
),
);
}
});
}
}
// Standard React on* listeners, i.e. onClick or onClickCapture
if (reactEventName !== null) {
const listener = getListener(instance, reactEventName);
if (listener != null) {
listeners.push(
createDispatchListener(instance, listener, lastHostComponent),
);
}
}
} else if (
enableCreateEventHandleAPI &&
enableScopeAPI &&
tag === ScopeComponent &&
lastHostComponent !== null &&
stateNode !== null
) {
// Scopes
const reactScopeInstance = stateNode;
const eventHandlerListeners = getEventHandlerListeners(
reactScopeInstance,
);
if (eventHandlerListeners !== null) {
eventHandlerListeners.forEach(entry => {
if (
entry.type === nativeEventType &&
entry.capture === inCapturePhase
) {
listeners.push(
createDispatchListener(
instance,
entry.callback,
(lastHostComponent: any),
),
);
}
});
}
}
if (accumulateTargetOnly) {
break;
}
instance = instance.return;
}
return listeners;
}
processDispatchQueue
依次执行dispatchQueue的事件触发
export function processDispatchQueue(
dispatchQueue: DispatchQueue,
eventSystemFlags: EventSystemFlags,
): void {
const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
for (let i = 0; i < dispatchQueue.length; i++) {
const {event, listeners} = dispatchQueue[i];
processDispatchQueueItemsInOrder(event, listeners, inCapturePhase);
}
rethrowCaughtError();
}
function processDispatchQueueItemsInOrder(
event: ReactSyntheticEvent,
dispatchListeners: Array<DispatchListener>,
inCapturePhase: boolean,
): void {
let previousInstance;
if (inCapturePhase) {
for (let i = dispatchListeners.length - 1; i >= 0; i--) {
const {instance, currentTarget, listener} = dispatchListeners[i];
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
} else {
for (let i = 0; i < dispatchListeners.length; i++) {
const {instance, currentTarget, listener} = dispatchListeners[i];
if (instance !== previousInstance && event.isPropagationStopped()) {
return;
}
executeDispatch(event, listener, currentTarget);
previousInstance = instance;
}
}
}
function executeDispatch(
event: ReactSyntheticEvent,
listener: Function,
currentTarget: EventTarget,
): void {
const type = event.type || 'unknown-event';
event.currentTarget = currentTarget;
invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
event.currentTarget = null;
}
3.9 context
这是一个发布订阅者模型,当context变化通知 订阅的fiber
export function createContext<T>(
defaultValue: T,
calculate<T> {
ChangedBits: ?(a: T, b: T) => number,
): ReactContext
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
_currentValue: defaultValue,
_currentValue2: defaultValue,
_threadCount: 0,
Provider: (null: any),
Consumer: (null: any),
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
let hasWarnedAboutUsingNestedContextConsumers = false;
let hasWarnedAboutUsingConsumerProvider = false;
let hasWarnedAboutDisplayNameOnConsumer = false;
context.Consumer = context;
return context;
}
发布 createContext
传递 context,有以下几种方式:
- <TestContext.Provider value={value}> </TestContext.Provider>
- static childContextTypes + getChildContext
<TestContext.Provider
该标签给节点增加属性 $$typeof: REACT_PROVIDER_TYPE,在创建fiber过程中,fiberTag = ContextProvide。
beginWork -> case ContextProvider -> updateContextProvider:
- 我们可以用一个
valueStack记录我们的context,在render阶段pushProvider,在commit阶段popProvider,在每个具体的层级能根据valueCursor取当前value;
function updateContextProvider(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
const providerType: ReactProviderType<any> = workInProgress.type;
const context: ReactContext<any> = providerType._context;
const newProps = workInProgress.pendingProps;
const oldProps = workInProgress.memoizedProps;
const newValue = newProps.value;
pushProvider(workInProgress, newValue);
if (oldProps !== null) {
const oldValue = oldProps.value;
const changedBits = calculateChangedBits(context, newValue, oldValue);
if (changedBits === 0) {
if (
oldProps.children === newProps.children &&
!hasLegacyContextChanged()
) {
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderLanes,
);
}
} else {
propagateContextChange(workInProgress, context, changedBits, renderLanes);
}
}
const newChildren = newProps.children;
reconcileChildren(current, workInProgress, newChildren, renderLanes);
return workInProgress.child;
}
completeWork -> case ContextProvider -> updateContextProvider:
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case ...
case ContextProvider:
popProvider(workInProgress);
bubbleProperties(workInProgress);
return null;
}
}
getChildContext
在解析Class组件时,如果构造类存在 childContextTypes 属性
- finishClassComponent -> invalidateContextProvider 更改当前 valueStack
function invalidateContextProvider(
workInProgress: Fiber,
type: any,
didChange: boolean,
): void {
if (disableLegacyContext) {
return;
} else {
const instance = workInProgress.stateNode;
if (didChange) {
const mergedContext = processChildContext(
workInProgress,
type,
previousContext,
);
instance.__reactInternalMemoizedMergedChildContext = mergedContext;
pop(didPerformWorkStackCursor, workInProgress);
pop(contextStackCursor, workInProgress);
push(contextStackCursor, mergedContext, workInProgress);
push(didPerformWorkStackCursor, didChange, workInProgress);
} else {
pop(didPerformWorkStackCursor, workInProgress);
push(didPerformWorkStackCursor, didChange, workInProgress);
}
}
}
- updateContainer -> getContextForSubtree 获取context
function processChildContext(
fiber: Fiber,
type: any,
parentContext: Object,
): Object {
if (disableLegacyContext) {
return parentContext;
} else {
const instance = fiber.stateNode;
const childContextTypes = type.childContextTypes;
if (typeof instance.getChildContext !== 'function') {
return parentContext;
}
const childContext = instance.getChildContext();
return {...parentContext, ...childContext};
}
}
订阅
下面几种情况会使用到context
- useContext
- static contextType = TestContext
- <TestContext.consumer> 标签
在 begainWork 解析 class组件 、ContextProvider 组件、函数组件useContext过程中 会执行 readContext,会在fiber的dependencies增加context链表
export function readContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
const contextItem = {
context: ((context: any): ReactContext<mixed>),
observedBits,
next: null,
};
if (lastContextDependency === null) {
lastContextDependency = contextItem;
currentlyRenderingFiber.dependencies = {
lanes: NoLanes,
firstContext: contextItem,
responders: null,
};
} else {
lastContextDependency = lastContextDependency.next = contextItem;
}
}
return isPrimaryRenderer ? context._currentValue : context._currentValue2;
}
通知订阅者 propagateContextChange
遍历fiber,如果订阅context的fiber具有dependencies 链表,dependencies.observedBits 判断是否需要更新
export function propagateContextChange(
workInProgress: Fiber,
context: ReactContext<mixed>,
changedBits: number,
renderLanes: Lanes,
): void {
let fiber = workInProgress.child;
if (fiber !== null) {
fiber.return = workInProgress;
}
while (fiber !== null) {
let nextFiber;
const list = fiber.dependencies;
if (list !== null) {
nextFiber = fiber.child;
let dependency = list.firstContext;
while (dependency !== null) {
if (
dependency.context === context &&
(dependency.observedBits & changedBits) !== 0
) {
if (fiber.tag === ClassComponent) {
const update = createUpdate(
NoTimestamp,
pickArbitraryLane(renderLanes),
);
update.tag = ForceUpdate;
enqueueUpdate(fiber, update);
}
fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
const alternate = fiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
}
scheduleWorkOnParentPath(fiber.return, renderLanes);
list.lanes = mergeLanes(list.lanes, renderLanes);
break;
}
dependency = dependency.next;
}
} else if (fiber.tag === ContextProvider) {
nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
}
if (nextFiber !== null) {
nextFiber.return = fiber;
} else {
nextFiber = fiber;
while (nextFiber !== null) {
if (nextFiber === workInProgress) {
nextFiber = null;
break;
}
const sibling = nextFiber.sibling;
if (sibling !== null) {
sibling.return = nextFiber.return;
nextFiber = sibling;
break;
}
nextFiber = nextFiber.return;
}
}
fiber = nextFiber;
}
}
3.10 ref
export default function App() {
const classRef = useRef();
const functionRef = useRef();
return (
<>
<MyClass ref={classRef} />
<MyCounter ref={functionRef} />
</>
);
}
useRef
function mountRef<T>(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
hook.memoizedState = ref;
return ref;
}
function updateRef<T>(initialValue: T): {|current: T|} {
const hook = updateWorkInProgressHook();
return hook.memoizedState;
}
createRef
export function createRef(): RefObject {
const refObject = {
current: null,
};
return refObject;
}
<MyClass ref={classRef} />
- ref为函数 ,执行 ref(instanceToUse)
- 否则 ref.current = instance
recursivelyCommitLayoutEffects -> commitAttachRef
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;
if (ref !== null) {
const instance = finishedWork.stateNode;
let instanceToUse;
switch (finishedWork.tag) {
case HostComponent:
instanceToUse = getPublicInstance(instance);
break;
default:
instanceToUse = instance;
}
if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
instanceToUse = instance;
}
if (typeof ref === 'function') {
ref(instanceToUse);
} else {
ref.current = instanceToUse;
}
}
}
函数组件 useImperativeHandle
使用方法
function MyCounter(props, ref) {
const [count, setCount] = useState(0);
useImperativeHandle(ref, () => ({ resetCount: resetCount, }));
function resetCount() { setCount(0); }
function increment() { setCount(count + 1); }
return ( <div> <button onClick={increment}>+1</button> </div> );
}
- 在commit阶段flushPassiveEffect函数中执行memoizedState链表的函数
function mountImperativeHandle<T>(
ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
create: () => T,
deps: Array<mixed> | void | null,
): void {
const effectDeps =
deps !== null && deps !== undefined ? deps.concat([ref]) : null;
return mountEffectImpl(
UpdateEffect,
HookLayout,
imperativeHandleEffect.bind(null, create, ref),
effectDeps,
);
}
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,
);
}
function imperativeHandleEffect<T>(
create: () => T,
ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
) {
if (typeof ref === 'function') {
const refCallback = ref;
const inst = create();
refCallback(inst);
return () => {
refCallback(null);
};
} else if (ref !== null && ref !== undefined) {
const refObject = ref;
const inst = create();
refObject.current = inst;
return () => {
refObject.current = null;
};
}
}
欢迎关注我的前端自检清单,我和你一起成长