react源码版本是17.0.2
1. react 入口
react入口函数有三种模式:
- legacy(传统)模式:
ReactDOM.render(, rootNode)
;LegacyRoot = 0。
这是当前 React app 使用的方式。构建dom的过程是同步的,所以在render阶段,如果diff特别耗时,会导致js一直阻塞高优先级的任务(例如用户的点击事件),表现为页面的卡顿,无法响应。
- blocking(阻塞)模式:
ReactDOM.createBlockingRoot(rootNode).render()
;BlockingRoot = 1。目前正在实验中。作为迁移到 concurrent 模式的第一个步骤。
最新的react文档指出,下面所有关于 “blocking 模式” 和 createBlockingRoot 的说法都已过时,应忽略。
- concurrent(并发)模式:
ReactDOM.createRoot(rootNode).render()
;ConcurrentRoot = 2。
目前在实验中,未来稳定之后,打算作为 React 的默认开发模式。将长任务分成一个个小任务,每一帧都预留出时间片(5ms)供react执行js代码,通过调度实现异步可中断、带优先级的更新任务.高优先级的任务可以打断低优先级任务。当前时间片时间用完,就暂停react的任务,将主线程控制权交还给浏览器使其有时间渲染 UI。react等待下一帧的时间片,再继续被打断的任务。
下图为入口函数的执行过程:
1.1 react 入口函数
legacy模式入口
ReactDOM.render(<App />, rootElement)
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
| function render(element, container, callback) { return legacyRenderSubtreeIntoContainer(null, element, container, false, callback); } function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) { var root = container._reactRootContainer; var fiberRoot; if (!root) { root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate); fiberRoot = root._internalRoot; if (typeof callback === 'function') { var originalCallback = callback; callback = function () { var instance = getPublicRootInstance(fiberRoot); originalCallback.call(instance); }; } unbatchedUpdates(function () { children = element = { $$typeof: Symbol(react.element), key: null, props: {}, ref: null, type: App } updateContainer(children, fiberRoot, parentComponent, callback); }); } else { fiberRoot = root._internalRoot; if (typeof callback === 'function') { var _originalCallback = callback; callback = function () { var instance = getPublicRootInstance(fiberRoot); _originalCallback.call(instance); }; } updateContainer(children, fiberRoot, parentComponent, callback); }
return getPublicRootInstance(fiberRoot); } function legacyCreateRootFromDOMContainer(container, forceHydrate) { var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); if (!shouldHydrate) { var rootSibling; while (rootSibling = container.lastChild) { container.removeChild(rootSibling); } } return createLegacyRoot(container, shouldHydrate ? { hydrate: true } : undefined); } function createLegacyRoot(container, options) { return new ReactDOMBlockingRoot(container, LegacyRoot, options); } function ReactDOMBlockingRoot(container, tag, options) { this._internalRoot = createRootImpl(container, tag, options); }
|
concurrent模式入口
ReactDOM.createRoot(rootNode).render(<App/>)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function createRoot(container, options) { return new ReactDOMRoot(container, options); }
function ReactDOMRoot(container, options) { this._internalRoot = createRootImpl(container, ConcurrentRoot, options); }
ReactDOMRoot.prototype.render = ReactDOMBlockingRoot.prototype.render = function (children) { var root = this._internalRoot; updateContainer(children, root, null, null); }
|
创建fiberRoot 和 rootFiber的公共逻辑
- 调用createRootImpl,会调用到createFiberRoot创建fiberRootNode,然后调用createHostRootFiber创建rootFiber
其中fiberRootNode是整个项目的根节点,包含应用挂载的目标节点,记录整个应用更新过程的各种信息
rootFiber是当前应用挂载的节点,即ReactDOM.render调用后的根节点
- fiberRoot和rootFiber的关系
fiberRoot.current = rootFiber
rootFiber.stateNode = fiberRoot
rootFiber.child = 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 130 131 132 133 134 135
| function createRootImpl(container, tag, options) { var hydrate = options != null && options.hydrate === true; var hydrationCallbacks = options != null && options.hydrationOptions || null; var root = createContainer(container, tag, hydrate); { var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container; listenToAllSupportedEvents(rootContainerElement); } return root; }
function createContainer(containerInfo, tag, hydrate, hydrationCallbacks) { return createFiberRoot(containerInfo, tag, hydrate); }
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) { var root = new FiberRootNode(containerInfo, tag, hydrate); var uninitializedFiber = createHostRootFiber(tag); root.current = uninitializedFiber; uninitializedFiber.stateNode = root;
initializeUpdateQueue(uninitializedFiber); return root; }
function initializeUpdateQueue(fiber) { var queue = { baseState: fiber.memoizedState, firstBaseUpdate: null, lastBaseUpdate: null, shared: { pending: null }, effects: null }; fiber.updateQueue = queue; }
function FiberRootNode(containerInfo, tag, hydrate) { this.tag = tag; this.current = null; this.containerInfo = containerInfo; 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.finishedLanes = NoLanes; }
function createHostRootFiber(tag) {
var mode; if (tag === ConcurrentRoot) { mode = ConcurrentMode | BlockingMode | StrictMode; } else if (tag === BlockingRoot) { mode = BlockingMode | StrictMode; } else { mode = NoMode; } if ( isDevToolsPresent) { mode |= ProfileMode; } return createFiber(HostRoot, null, null, mode); } var createFiber = function (tag, pendingProps, key, mode) { return new FiberNode(tag, pendingProps, key, mode); }; function FiberNode( tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode, ) { this.tag = tag; this.key = key; this.elementType = null; this.type = null; this.stateNode = null; 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.mode = mode; this.flags = NoFlags; this.nextEffect = null; this.firstEffect = null; this.lastEffect = null; this.lanes = NoLanes; this.childLanes = NoLanes; this.alternate = null; } function initializeUpdateQueue(fiber) { var queue = { baseState: fiber.memoizedState, baseQueue: null, shared: { pending: null }, effects: null }; fiber.updateQueue = queue; }
|
补充
执行非批量更新的操作 unbatchedUpdates
- 保存初始上下文,增加非批量上下文,删除批量更新上下文, 执行函数, 重置上下文,重置时间和执行同步队列
- 非批量更新下,会走同步更新逻辑,因为是第一次更新,需要用户尽快看到页面内容,不走异步更新的逻辑
- 执行完fn,需要恢复之前的执行环境 prevExecutionContext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function unbatchedUpdates(fn, a) { var prevExecutionContext = executionContext; executionContext &= ~BatchedContext; executionContext |= LegacyUnbatchedContext; try { return fn(a); } finally { executionContext = prevExecutionContext; } }
|
1.2 创建更新 updateContainer
- 获取rootFiber
- 获取当前时间
- 获取更新优先级lane
- 创建更新
- 将更新加入更新队列
- 更新任务调度
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
| function updateContainer(element, container, parentComponent, callback) { var current$1 = container.current; var eventTime = requestEventTime(); var lane = requestUpdateLane(current$1); var update = createUpdate(eventTime, lane);
update.payload = { element: element }; callback = callback === undefined ? null : callback; if (callback !== null) { update.callback = callback; } enqueueUpdate(current$1, update); scheduleUpdateOnFiber(current$1, lane, eventTime); return lane; }
|
1)获取当前时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function requestEventTime() { if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { return now(); } if (currentEventTime !== NoTimestamp) { return currentEventTime; } currentEventTime = now(); return currentEventTime; }
var initialTimeMs = performance.now()
var now = initialTimeMs < 10000 ? performance.now : function () { return performance.now() - initialTimeMs; };
|
2)获取更新优先级lane,requestUpdateLane
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
| function requestUpdateLane(fiber) {
var mode = fiber.mode; if ((mode & BlockingMode) === NoMode) { return SyncLane; } else if ((mode & ConcurrentMode) === NoMode) { return getCurrentPriorityLevel() === ImmediatePriority$1 ? SyncLane : SyncBatchedLane; } if (currentEventWipLanes === NoLanes) { currentEventWipLanes = workInProgressRootIncludedLanes; } var schedulerPriority = getCurrentPriorityLevel(); var lane; if ( (executionContext & DiscreteEventContext) !== NoContext && schedulerPriority === UserBlockingPriority$2) { lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes); } else { var schedulerLanePriority = schedulerPriorityToLanePriority(schedulerPriority); lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes); }
return lane; }
function findUpdateLane(lanePriority, wipLanes) { switch (lanePriority) { case NoLanePriority: break; case SyncLanePriority: return SyncLane; case SyncBatchedLanePriority: return SyncBatchedLane; case InputDiscreteLanePriority: { var _lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes); if (_lane === NoLane) { return findUpdateLane(InputContinuousLanePriority, wipLanes); } return _lane; } case InputContinuousLanePriority: { var _lane2 = pickArbitraryLane(InputContinuousLanes & ~wipLanes); if (_lane2 === NoLane) { return findUpdateLane(DefaultLanePriority, wipLanes); } return _lane2; } case DefaultLanePriority: {
var _lane3 = pickArbitraryLane(DefaultLanes & ~wipLanes); if (_lane3 === NoLane) { _lane3 = pickArbitraryLane(TransitionLanes & ~wipLanes); if (_lane3 === NoLane) { _lane3 = pickArbitraryLane(DefaultLanes); } } return _lane3; } case TransitionPriority: case RetryLanePriority: break; case IdleLanePriority: var lane = pickArbitraryLane(IdleLanes & ~wipLanes);
if (lane === NoLane) { lane = pickArbitraryLane(IdleLanes); } return lane; } }
function pickArbitraryLane(lanes) {
return getHighestPriorityLane(lanes); }
|
getCurrentPriorityLevel:Scheduler调度优先级转换为react-dom中Scheduler调度优先级
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
| function getCurrentPriorityLevel() { switch (Scheduler_getCurrentPriorityLevel()) { case Scheduler_ImmediatePriority: return ImmediatePriority$1;
case Scheduler_UserBlockingPriority: return UserBlockingPriority$2;
case Scheduler_NormalPriority: return NormalPriority$1;
case Scheduler_LowPriority: return LowPriority$1;
case Scheduler_IdlePriority: return IdlePriority$1;
default: } }
function unstable_getCurrentPriorityLevel() { return currentPriorityLevel; }
|
schedulerPriorityToLanePriority:调度优先级转换为更新任务优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function schedulerPriorityToLanePriority(schedulerPriorityLevel) { switch (schedulerPriorityLevel) { case ImmediatePriority: return SyncLanePriority; case UserBlockingPriority: return InputContinuousLanePriority; case NormalPriority: case LowPriority: return DefaultLanePriority; case IdlePriority: return IdleLanePriority; default: return NoLanePriority; } }
|
3)创建更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function createUpdate(eventTime, lane) { var update = { eventTime: eventTime, lane: lane,
tag: UpdateState, payload: null, callback: null, next: null }; return update; }
|
4)将更新插入到更新链表
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
| function enqueueUpdate(fiber, update) {
var updateQueue = fiber.updateQueue; if (updateQueue === null) { return; }
var sharedQueue = updateQueue.shared; var pending = sharedQueue.pending; if (pending === null) {
update.next = update; } else { update.next = pending.next; pending.next = update; } sharedQueue.pending = update; }
|