setState
setState是我们常用的更改状态的方法,state的改变标示者组件要更新。但调用setState是否就触发组件更新呢?
class App extends Component {
state={count: 0}
batchUpdates = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (<div onClick={this.batchUpdates}>{`点击更改状态${ this.state.count }`} </div>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
inst为App的实例,值为:
事件调用此处不再叙述,执行事件方法时,this.setState会调用 Component.prototype.setState。
Component.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
var classComponentUpdater = {
//...
enqueueSetState: function enqueueSetState(inst, payload, callback) {
var fiber = get(inst);
var currentTime = requestCurrentTime();
var expirationTime = computeExpirationForFiber(currentTime, fiber);
var update = createUpdate(expirationTime);
update.payload = payload;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
flushPassiveEffects();
enqueueUpdate(fiber, update);
scheduleWork(fiber, expirationTime);
}
//....
}
payload 为 setState 中的 partialState,此处的值为{ count: 1 };callback 为undefined;
enqueueUpdate
createUpdate(expirationTime) 和 update.payload = payload 会生成 update对象,值为:
flushPassiveEffects 用来刷新 优先级低 的effects。
初创的fiber没有 updateQueue ,当调用enqueueUpdate 会创建更新队列。执行代码如下:
function enqueueUpdate(fiber, update) {
// Update queues are created lazily.
var alternate = fiber.alternate;// alternate 为null
var queue1 = void 0;
var queue2 = void 0;
if (alternate === null) {
// There's only one fiber.
queue1 = fiber.updateQueue;
queue2 = null;
if (queue1 === null) {
//fiber.memoizedState 的值为 { count: 0 },之前的值
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
}
} else { //... }
if (queue2 === null || queue1 === queue2) {
appendUpdateToQueue(queue1, update);
} else { //... }
}
function createUpdateQueue(baseState) {
var queue = {
baseState: baseState,
firstUpdate: null,
lastUpdate: null,
firstCapturedUpdate: null,
lastCapturedUpdate: null,
firstEffect: null,
lastEffect: null,
firstCapturedEffect: null,
lastCapturedEffect: null
};
return queue;
}
function appendUpdateToQueue(queue, update) {
// Append the update to the end of the list.
if (queue.lastUpdate === null) {
// Queue is empty
queue.firstUpdate = queue.lastUpdate = update;
} else {
queue.lastUpdate.next = update;
queue.lastUpdate = update;
}
}
createUpdateQueue 创建的队列值为:
执行完appendUpdateToQueue 后,得到的query的值为:
当前 fiber为type 为 ƒ App(),此时的队列的值保存在 fiber.updateQueue。
scheduleWork
scheduleWork 可参考 updateContainer 中scheduleWork。与updateContainer不同,在调用requestWork时,由于isBatchingUpdates 为true,直接返回没有执行到 performSyncWork。
function requestWork(root, expirationTime) {
addRootToSchedule(root, expirationTime);
if (isRendering) {
return;
}
if (isBatchingUpdates) {// isBatchingUpdates为true
if (isUnbatchingUpdates) {
nextFlushedRoot = root;
nextFlushedExpirationTime = Sync;
performWorkOnRoot(root, Sync, false);
}
return;//直接返回
}
if (expirationTime === Sync) {
performSyncWork();
} else {
scheduleCallbackWithExpirationTime(root, expirationTime);
}
}
此处有一个关键点是 addRootToSchedule,将 root 添加到 schedule 中,root只添加一次。addRootToSchedule在render中介绍过,传送门。
多次调用setState
class App extends Component {
state = { val: 0 }
batchUpdates = () => {
this.setState({ val: this.state.val + 1 });console.log(this.state.val);
this.setState({ val: this.state.val + 1 });console.log(this.state.val);
this.setState({ val: this.state.val + 1 });console.log(this.state.val);
}
render() {
return (<div onClick={this.batchUpdates}>{`Counter is ${this.state.val}`} </div>)
}
}
每调执行 this.setState 就调用 classComponentUpdater.enqueueSetState方法,生成 fiber.updateQueue队列。当执行完 batchUpdates 方法时,得到两个fiber的updateQueue为:
执行完 batchUpdates 方法时state并没有更新,只创建了队列。此时 queue 的 payload 都为{ val: 1 }。
队列值的变更
多次调用setState和 单次调用setState 是相似的,此处以单次作为示例说明,队列的执行不再赘述。
在 updateClassInstance -> processUpdateQueue 中会将 workInProgress 队列中的 firstUpdate、lastUpdate 设置为null,此时 workInProgress.updateQueue 为:
但此时 workInProgress.alternate.updateQueue 中还是有队列的,值为:
更新完成后,若再次更改state,在 enqueueUpdate 创建队列queue1 、queue2时,一个是只有更新队列,一个是 next 指向更新队列。然后进入更新。
在更新的过程中,进入 createWorkInProgress 中会设置 workInProgress.updateQueue = current.updateQueue 。这样就使 workInProgress.updateQueue的队列(带next的队列)被遗弃,直接使用current.updateQueue。这个涉及 dom与fiber 和 current 与 workInProgress 交换,具体请参考 performWork 。
更新
更新setState 通常要使用派发事件并调用interactiveUpdates$1(事件派发中的)。
function interactiveUpdates$1(fn, a, b) {
if (!isBatchingUpdates && !isRendering &&
lowestPriorityPendingInteractiveExpirationTime !== NoWork) {
// Synchronously flush pending interactive updates.
performWork(lowestPriorityPendingInteractiveExpirationTime, false);
lowestPriorityPendingInteractiveExpirationTime = NoWork;
}
var previousIsBatchingUpdates = isBatchingUpdates;
isBatchingUpdates = true;
try {
// 执行事件的方法
return scheduler.unstable_runWithPriority(scheduler.unstable_UserBlockingPriority,
function () { return fn(a, b); });
} finally {
isBatchingUpdates = previousIsBatchingUpdates;
if (!isBatchingUpdates && !isRendering) {
performSyncWork();
}
}
}
在 scheduler.unstable_runWithPriority 执行完事件后,就进入到 finally 中,此时 isBatchingUpdates 为false,
isRendering 为 false,直接执行 performSyncWork。
performSyncWork
Initial mount 和事件都是同步更新。在render中已接触过performSyncWork,但 render 和 更新略有不同。调用栈如下:
performWork
function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
findHighestPriorityRoot();// 查找是否有更新;获得nextFlushedRoot等值
if (isYieldy) {
{//...有 查找到 nextFlushedRoot 、nextFlushedExpirationTime
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
findHighestPriorityRoot();
}
finishRendering();//没有更新,则结束渲染
}
有点熟悉的味道,performWorkOnRoot 中执行的和之前的基本相同。
function performWorkOnRoot(root, expirationTime, isYieldy) {
isRendering = true; // Check if this is async work or sync/expired work.
if (!isYieldy) {
var finishedWork = root.finishedWork;
if (finishedWork !== null) {
// This root is already complete. We can commit it.
completeRoot(root, finishedWork, expirationTime);
} else {
root.finishedWork = null;
var timeoutHandle = root.timeoutHandle;
if (timeoutHandle !== noTimeout) {
root.timeoutHandle = noTimeout;
cancelTimeout(timeoutHandle);
}
renderRoot(root, isYieldy);//渲染root
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// We've completed the root. Commit it.
completeRoot(root, finishedWork, expirationTime);
}
}
} else {//...}
isRendering = false;
}
更新时会从ReactRoot开始,创建整棵React Tree树,在渲染的过程中复用fiber。
renderRoot
function renderRoot(root: FiberRoot, isYieldy: boolean): void {
flushPassiveEffects();
isWorking = true;
if (
expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null
) {
// Reset the stack and start working from the root.
resetStack();
nextRoot = root;
nextRenderExpirationTime = expirationTime;
//更新,也是走此处,注意此时 nextUnitOfWork 被赋值
nextUnitOfWork = createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime);
root.pendingCommitExpirationTime = NoWork;
}
var didFatal = false;
startWorkLoopTimer(nextUnitOfWork); //开始 "(React Tree Reconciliation)"
do {
try { workLoop(isYieldy); } catch (thrownValue) {//异常处理,此处省略 }
break;
} while (true);
// We're done performing work. Time to clean up.
isWorking = false;
ReactCurrentDispatcher.current = null;
resetContextDependences();
resetHooks();
// We completed the whole tree.
const didCompleteRoot = true;
stopWorkLoopTimer(interruptedBy, didCompleteRoot);// 删除 "(React Tree Reconciliation)"
//....
// Ready to commit.
onComplete(root, rootWorkInProgress, expirationTime);
}
createWorkInProgress
在更新中,这个是我们接触较多的方法。第一次执行时 current.alternate 被赋值为 workInProgress。
function createWorkInProgress(current, pendingProps, expirationTime) {
var workInProgress = current.alternate;
if (workInProgress === null) {
workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;
workInProgress.alternate = current;
current.alternate = workInProgress;//一直保存 work.
} else {
workInProgress.pendingProps = pendingProps; // We already have an alternate.
// Reset the effect tag.
workInProgress.effectTag = NoEffect; // The effect list is no longer valid.
workInProgress.nextEffect = null;
workInProgress.firstEffect = null;
workInProgress.lastEffect = null;
if (enableProfilerTimer) {
workInProgress.actualDuration = 0;
workInProgress.actualStartTime = -1;
}
}
workInProgress.childExpirationTime = current.childExpirationTime;
workInProgress.expirationTime = current.expirationTime;//此时是0
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;//当前的props
workInProgress.memoizedState = current.memoizedState;//当前的state
workInProgress.updateQueue = current.updateQueue;// 当前的更新队列。setState时改的就是fiber队列
workInProgress.contextDependencies = current.contextDependencies;
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
if (enableProfilerTimer) {
workInProgress.selfBaseDuration = current.selfBaseDuration;
workInProgress.treeBaseDuration = current.treeBaseDuration;
}
return workInProgress;
}
createWorkInProgress 是在更新时被调用。在更新过程中createWorkInProgress 起什么作用呢?
第一次是创建fiber,也就是current。
第二次current.alternate为null,以pendingProps等属性创建fiber,并复制current的属性。返回的workInProgress fiber 中新旧属性、新队列等信息。
第三次 current1 为第二次的workInProgress,current1.alternate为第二次的 current,此时以current1为模板得到 workInProgress2(被修改属性后的current)。返回workInProgress2就包含新旧属性、更新队列等信息。
第四次 current2是第三次的 workInProgress2,current2.alternate为第三次current1,此时以current2为模板得到workInProgress3(修改属性后的current1),返回workInProgress3中包含新旧属性、更新队列等信息。
第一次 第二次 第三次 第4次
current current workInProgress current1 workInProgress2 current2 workInProgress3
——- ——- ——————- ———- ————————- ————— —————————
|| || | | || || |
|| || | | || || |
|| || | | || || |
|| || | | || || |
|| || | | || || |
|| || | | || || |
在setState形成的队列保留在current上,在修改某属性值后,workInProgress添加新属性,无修改则保留之前的属性值。 队列是复用 current 上的 update。createWorkInProgress复用之前的fiber,适当修改属性,形成新的fiber。
createWorkInProgress 总的值是复制的,若值是对象,则可实时看到最新值。
dom 对应的fiber
生成的dom对应有相关的fiber,而这个fiber指向上面的 || 表示的树。调用 setState 执行 enqueueSetState时,会发现fiber中 memoizedState 并不是最新值。
var classComponentUpdater = {
isMounted: isMounted,
enqueueSetState: function enqueueSetState(inst, payload, callback) {
var fiber = get(inst);//此实例 inst 的fiber,指向上面的 || 标识的树。
var currentTime = requestCurrentTime();
var expirationTime = computeExpirationForFiber(currentTime, fiber);
var update = createUpdate(expirationTime);
update.payload = payload;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
flushPassiveEffects();
enqueueUpdate(fiber, update);
scheduleWork(fiber, expirationTime);
}
}
假如有以下示例:
class App extends Component {
state={
count: 0
}
onClickFn = ()=>{
this.setState({ count: this.state.count + 1 })
}
render() {
return (<div id="appId">
<span onClick={ this.onClickFn }点击 { this.state.count }</span>
</div>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
此处只统计在 enqueueSetState 中的 var fiber = get(inst) 对象的值,来说明dom与fiber 及 workInProgress的关系。
state | 操作 | current(||) memoizedState 值 | 结果 state的值 |
---|---|---|---|
{ count: 0 } | { count: 0 } | ||
{ count: 0 } | 点击span | { count: 0 } | { count: 1 } |
{ count: 1 } | 点击span | { count: 0 } | { count: 2 } |
{ count: 2 } | 点击span | { count: 2 } | { count: 3 } |
{ count: 3 } | 点击span | { count: 2 } | { count: 4 } |
{ count: 4 } | 点击span | { count: 4 } | { count: 5 } |
此处的 fiber 是上一次计算的结果。由 createWorkInProgress 中的 || 与 | 交换可知,|| 在第三次 和 第五次 看到 最新值。
交换
renderRoot 完成整棵树后,root.current.alternate 是当前的rootWorkInProgress,然后在onComplete中记录值;
交换是在 commitRoot 中完成,代码是 root.current = rootWorkInProgress。由于值是对象类型,且是相互复用,所以一个上面改,两个都变。
function performWorkOnRoot(root, expirationTime, isYieldy) {
//...
renderRoot(root, isYieldy);
finishedWork = root.finishedWork;
if (finishedWork !== null) {
completeRoot(root, finishedWork, expirationTime);
}
//...
}
function renderRoot(){
//...
var rootWorkInProgress = root.current.alternate;//此处为交换点
//...
onComplete(root, rootWorkInProgress, expirationTime);
}
function onComplete(root, finishedWork, expirationTime) {
root.pendingCommitExpirationTime = expirationTime;
root.finishedWork = finishedWork;
}
function completeRoot(root, finishedWork, expirationTime) {
//...
root.finishedWork = null; //此处清空
//...
commitRoot(root, finishedWork);
}
function commitRoot(){
//...省略代码
root.current = finishedWork; //root为 FiberRoot
//...
}
beginWork
此时 renderExpirationTime是 1073741823,workInProgress.expirationTime 是0;current$$1是fiber;
function beginWork(
current: Fiber | null, workInProgress: Fiber, renderExpirationTime: ExpirationTime
): Fiber | null {
const updateExpirationTime = workInProgress.expirationTime;
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (oldProps !== newProps || hasLegacyContextChanged()) {
didReceiveUpdate = true;
} else if (updateExpirationTime < renderExpirationTime) {
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;
}
}
// 更新执行此处
return bailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime);
}
}
}
在beginWork中会复用之前已完成的work,而不是重新创建fiber树。
function bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime) {
cancelWorkTimer(workInProgress);
if (current$$1 !== null) {
workInProgress.contextDependencies = current$$1.contextDependencies;
}
if (enableProfilerTimer) {
stopProfilerTimerIfRunning(workInProgress);
}
var childExpirationTime = workInProgress.childExpirationTime;
if (childExpirationTime < renderExpirationTime) {
return null;
} else {
cloneChildFibers(current$$1, workInProgress);//clone子项
return workInProgress.child;//返回第一项
}
}
//clone子fiber
function cloneChildFibers(current$$1, workInProgress) {
if (workInProgress.child === null) { return; }
var currentChild = workInProgress.child;//拿到子fiber
//重点,复用的是currentChild.pendingProps,这就导致下次循环中oldProps === newProps 的出现
var newChild = createWorkInProgress(currentChild, currentChild.pendingProps,
currentChild.expirationTime);
workInProgress.child = newChild;
newChild.return = workInProgress;
while (currentChild.sibling !== null) {// 修改兄弟节点
currentChild = currentChild.sibling;
//复制兄弟节点,也是 pendingProps
newChild = newChild.sibling = createWorkInProgress(currentChild,
currentChild.pendingProps,
currentChild.expirationTime);
newChild.return = workInProgress;
}
newChild.sibling = null;//经过上面的循环,newChild已执行最后的兄弟节点
}
updateClassComponent
function updateClassComponent(current$$1, workInProgress,
Component, nextProps, renderExpirationTime) {
//...省略
var instance = workInProgress.stateNode;
var shouldUpdate = void 0;
if (instance === null) {
//...省略
} else if (current$$1 === null) {
//...省略
}else{
shouldUpdate = updateClassInstance(current$$1, workInProgress,
Component, nextProps, renderExpirationTime);
}
var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component,
shouldUpdate, hasContext, renderExpirationTime);
//...省略
return nextUnitOfWork;
}
在updateClassInstance 中计算 state,执行componentWillUpdate、getDerivedStateFromProps或componentWillReceiveProps。标记componentDidUpdate、getSnapshotBeforeUpdate。
function updateClassInstance(current, workInProgress, ctor, newProps, renderExpirationTime) {
var instance = workInProgress.stateNode;
var oldProps = workInProgress.memoizedProps;
instance.props = workInProgress.type === workInProgress.elementType ? oldProps :
resolveDefaultProps(workInProgress.type, oldProps);
var oldContext = instance.context;
var contextType = ctor.contextType;
var nextContext = void 0;
if (typeof contextType === 'object' && contextType !== null) {
nextContext = _readContext(contextType);
} else {
var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
}
var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' ||
typeof instance.getSnapshotBeforeUpdate === 'function';
// 新api getDerivedStateFromProps 和 componentWillReceiveProps 冲突
if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
typeof instance.componentWillReceiveProps === 'function')) {
if (oldProps !== newProps || oldContext !== nextContext) {
callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
}
}
resetHasForceUpdateBeforeProcessing();
var oldState = workInProgress.memoizedState;
var newState = instance.state = oldState;
var updateQueue = workInProgress.updateQueue;//当前对象上的更新队列
if (updateQueue !== null) {
processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
newState = workInProgress.memoizedState;//newState 诞生
}
if (oldProps === newProps && oldState === newState && !hasContextChanged()
&& !checkHasForceUpdateAfterProcessing()) {//通常更新不符合此条件
if (typeof instance.componentDidUpdate === 'function') {
if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
workInProgress.effectTag |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
workInProgress.effectTag |= Snapshot;
}
}
return false;
}
if (typeof getDerivedStateFromProps === 'function') {//执行 getDerivedStateFromProps
applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
newState = workInProgress.memoizedState;
}
var shouldUpdate = checkHasForceUpdateAfterProcessing() ||
checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps,
oldState, newState, nextContext);
if (shouldUpdate) {// 返回true和 false,有助于更新
if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function'
|| typeof instance.componentWillUpdate === 'function')) {
startPhaseTimer(workInProgress, 'componentWillUpdate');
if (typeof instance.componentWillUpdate === 'function') {
instance.componentWillUpdate(newProps, newState, nextContext);
}
if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
}
stopPhaseTimer();
}
if (typeof instance.componentDidUpdate === 'function') {//标记 Update
workInProgress.effectTag |= Update;
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {//标记 Snapshot
workInProgress.effectTag |= Snapshot;
}
} else {
// 如果 shouldComponentUpdate 返回false。
// 我们还应该更新memoized的props/state,以表明该工作可以重用
if (typeof instance.componentDidUpdate === 'function') {
if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {//条件
workInProgress.effectTag |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {//条件
workInProgress.effectTag |= Snapshot;
}
}
workInProgress.memoizedProps = newProps;
workInProgress.memoizedState = newState;
}
instance.props = newProps;// 更新实例的props
instance.state = newState;// 更新实例的state
instance.context = nextContext;// 更新context
return shouldUpdate;
}
在processUpdateQueue计算newState。 newProps是在执行组件render方法时得到。执行完此方法就更新了state、porps、context。
接着执行updateClassComponent 中 finishClassComponent。在finishClassComponent 中调用 instance.render()生成 nextChildren。之后与render时一样,调用reconcileChildren,调用栈如下:
调用与render 无异,只是在 reconcileChildren 中 current$$1.child有值,可以使用之前的fiber,最后也是使用createWorkInProgress 克隆fiber。
在 createWorkInProgress 中 current.alternate 为 nulll 会创建 fiber。第一次更新会为当前 fiber 的 alternate 赋值。
updateHostComponent
和render的updateHostComponent基本一致。beginWork的updateHostComponent 主要用于生成子 fiber。
function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
pushHostContext(workInProgress);
if (current$$1 === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
var type = workInProgress.type;
var nextProps = workInProgress.pendingProps;
var prevProps = current$$1 !== null ? current$$1.memoizedProps : null;
var nextChildren = nextProps.children;
var isDirectTextChild = shouldSetTextContent(type, nextProps);
if (isDirectTextChild) {
nextChildren = null;
} else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
workInProgress.effectTag |= ContentReset;
}
markRef(current$$1, workInProgress);
if (renderExpirationTime !== Never && workInProgress.mode & ConcurrentMode
&& shouldDeprioritizeSubtree(type, nextProps)) {
workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
return null;
}
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
return workInProgress.child;
}
updateHostText
function updateHostText(current, workInProgress) {
if (current === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
return null;
}
completeUnitOfWork
beginWork中 执行组件中的生命周期,并为组件标记effectTag。completeUnitOfWork 中则为HostText、HostComponent 标记effectTag。
function completeUnitOfWork(workInProgress) {
while (true) {
var current$$1 = workInProgress.alternate;
var returnFiber = workInProgress.return;
var siblingFiber = workInProgress.sibling;
//...
nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
//...
//... effect 链表
//...省略
}
return null;
}
function completeWork(current, workInProgress, renderExpirationTime) {
var newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
//...
case HostText:{
var newText = newProps;
if (current && workInProgress.stateNode != null) {
var oldText = current.memoizedProps;
updateHostText$1(current, workInProgress, oldText, newText);
} else {
//创建text
}
break;
}
case HostComponent:{
popHostContext(workInProgress);
var rootContainerInstance = getRootHostContainer();
var type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
updateHostComponent$1(current, workInProgress, type, newProps,
rootContainerInstance);
if (current.ref !== workInProgress.ref) {
markRef$1(workInProgress);
}
} else { //省略 创建 host Componennt }
break;
}
}
//....
}
completeWork 解析的就是 HostComponent、HostText。
updateHostComponent$1
updateHostComponent主要用于标记 dom 属性的变化,返回更新队列。
updateHostComponent$1 = function updateHostComponent$1(current, workInProgress, type,
newProps, rootContainerInstance) {
var oldProps = current.memoizedProps;
if (oldProps === newProps) { return; }
var instance = workInProgress.stateNode;
var currentHostContext = getHostContext();
var updatePayload = prepareUpdate(instance, type, oldProps, newProps,
rootContainerInstance, currentHostContext);
workInProgress.updateQueue = updatePayload;
if (updatePayload) {
markUpdate(workInProgress);
}
};
function prepareUpdate(domElement, type, oldProps,
newProps, rootContainerInstance, hostContext) {
return diffProperties(domElement, type, oldProps, newProps, rootContainerInstance);
}
//计算属性 packages\react-dom\src\client\ReactDOMComponent.js
function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {
// 对比STYLE$1
// 对比更新的属性
return updatePayload
}
updateHostText$1
updateHostText 用于标记文本的更新。
//文本更新
updateHostText$1 = function updateHostText$1(current, workInProgress, oldText, newText) {
if (oldText !== newText) { markUpdate(workInProgress);//标记有更新 }
}
function markUpdate(workInProgress) {
workInProgress.effectTag |= Update;
}
diff
diff基本是在React Tree Reconciliation中。
- beginWork 中更多偏向创建fiber。对组件classComponent:调用组件的生命周期、标记组件的生命周期,生成子项fiber等;对元素HostComponent:主要生成子项fiber;对于HostText,基本属于忽略状态。
- 在 reconcileChildren 生成子fiber时,会增删并标记fiber,删除的 fiber 只添加在 effect 链上,未在fibers中;
- 在 completeWork 中主要对比 HostComponent 的属性 、HostText的值。对于 HostComponent来说,调用 diffProperties 逐项对比属性的值,若属性有更新则标记,并更新属性保存在workInProgress.updateQueue中;对于HostText来说,对比oldText和newText的值,若不相等则标记更新;对于classComponent,基本是忽略。