此处只是更新一个组件,以演示更新过程。

setState

setState是我们常用的更改状态的方法,state的改变标示者组件要更新。但调用setState是否就触发组件更新呢?

  1. class App extends Component {
  2. state={count: 0}
  3. batchUpdates = () => {
  4. this.setState({
  5. count: this.state.count + 1
  6. })
  7. }
  8. render() {
  9. return (<div onClick={this.batchUpdates}>{`点击更改状态${ this.state.count }`} </div>)
  10. }
  11. }
  12. ReactDOM.render(<App/>, document.getElementById('root'));

inst为App的实例,值为:
image.png
事件调用此处不再叙述,执行事件方法时,this.setState会调用 Component.prototype.setState。

  1. Component.prototype.setState = function (partialState, callback) {
  2. this.updater.enqueueSetState(this, partialState, callback, 'setState');
  3. };
  4. var classComponentUpdater = {
  5. //...
  6. enqueueSetState: function enqueueSetState(inst, payload, callback) {
  7. var fiber = get(inst);
  8. var currentTime = requestCurrentTime();
  9. var expirationTime = computeExpirationForFiber(currentTime, fiber);
  10. var update = createUpdate(expirationTime);
  11. update.payload = payload;
  12. if (callback !== undefined && callback !== null) {
  13. update.callback = callback;
  14. }
  15. flushPassiveEffects();
  16. enqueueUpdate(fiber, update);
  17. scheduleWork(fiber, expirationTime);
  18. }
  19. //....
  20. }

payload 为 setState 中的 partialState,此处的值为{ count: 1 };callback 为undefined;

enqueueUpdate

createUpdate(expirationTime) 和 update.payload = payload 会生成 update对象,值为:
image.png
flushPassiveEffects 用来刷新 优先级低 的effects。
初创的fiber没有 updateQueue ,当调用enqueueUpdate 会创建更新队列。执行代码如下:

  1. function enqueueUpdate(fiber, update) {
  2. // Update queues are created lazily.
  3. var alternate = fiber.alternate;// alternate 为null
  4. var queue1 = void 0;
  5. var queue2 = void 0;
  6. if (alternate === null) {
  7. // There's only one fiber.
  8. queue1 = fiber.updateQueue;
  9. queue2 = null;
  10. if (queue1 === null) {
  11. //fiber.memoizedState 的值为 { count: 0 },之前的值
  12. queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
  13. }
  14. } else { //... }
  15. if (queue2 === null || queue1 === queue2) {
  16. appendUpdateToQueue(queue1, update);
  17. } else { //... }
  18. }
  19. function createUpdateQueue(baseState) {
  20. var queue = {
  21. baseState: baseState,
  22. firstUpdate: null,
  23. lastUpdate: null,
  24. firstCapturedUpdate: null,
  25. lastCapturedUpdate: null,
  26. firstEffect: null,
  27. lastEffect: null,
  28. firstCapturedEffect: null,
  29. lastCapturedEffect: null
  30. };
  31. return queue;
  32. }
  33. function appendUpdateToQueue(queue, update) {
  34. // Append the update to the end of the list.
  35. if (queue.lastUpdate === null) {
  36. // Queue is empty
  37. queue.firstUpdate = queue.lastUpdate = update;
  38. } else {
  39. queue.lastUpdate.next = update;
  40. queue.lastUpdate = update;
  41. }
  42. }

createUpdateQueue 创建的队列值为:
image.png
执行完appendUpdateToQueue 后,得到的query的值为:
image.png
当前 fiber为type 为 ƒ App(),此时的队列的值保存在 fiber.updateQueue。

scheduleWork

scheduleWork 可参考 updateContainer 中scheduleWork。与updateContainer不同,在调用requestWork时,由于isBatchingUpdates 为true,直接返回没有执行到 performSyncWork。

  1. function requestWork(root, expirationTime) {
  2. addRootToSchedule(root, expirationTime);
  3. if (isRendering) {
  4. return;
  5. }
  6. if (isBatchingUpdates) {// isBatchingUpdates为true
  7. if (isUnbatchingUpdates) {
  8. nextFlushedRoot = root;
  9. nextFlushedExpirationTime = Sync;
  10. performWorkOnRoot(root, Sync, false);
  11. }
  12. return;//直接返回
  13. }
  14. if (expirationTime === Sync) {
  15. performSyncWork();
  16. } else {
  17. scheduleCallbackWithExpirationTime(root, expirationTime);
  18. }
  19. }

此处有一个关键点是 addRootToSchedule,将 root 添加到 schedule 中,root只添加一次。addRootToSchedule在render中介绍过,传送门

多次调用setState

  1. class App extends Component {
  2. state = { val: 0 }
  3. batchUpdates = () => {
  4. this.setState({ val: this.state.val + 1 });console.log(this.state.val);
  5. this.setState({ val: this.state.val + 1 });console.log(this.state.val);
  6. this.setState({ val: this.state.val + 1 });console.log(this.state.val);
  7. }
  8. render() {
  9. return (<div onClick={this.batchUpdates}>{`Counter is ${this.state.val}`} </div>)
  10. }
  11. }

每调执行 this.setState 就调用 classComponentUpdater.enqueueSetState方法,生成 fiber.updateQueue队列。当执行完 batchUpdates 方法时,得到两个fiber的updateQueue为:

image.png
执行完 batchUpdates 方法时state并没有更新,只创建了队列。此时 queue 的 payload 都为{ val: 1 }。

队列值的变更

多次调用setState和 单次调用setState 是相似的,此处以单次作为示例说明,队列的执行不再赘述。
在 updateClassInstance -> processUpdateQueue 中会将 workInProgress 队列中的 firstUpdate、lastUpdate 设置为null,此时 workInProgress.updateQueue 为:
image.png
但此时 workInProgress.alternate.updateQueue 中还是有队列的,值为:
image.png
更新完成后,若再次更改state,在 enqueueUpdate 创建队列queue1 、queue2时,一个是只有更新队列,一个是 next 指向更新队列。然后进入更新。
在更新的过程中,进入 createWorkInProgress 中会设置 workInProgress.updateQueue = current.updateQueue 。这样就使 workInProgress.updateQueue的队列(带next的队列)被遗弃,直接使用current.updateQueue。这个涉及 dom与fiber 和 current 与 workInProgress 交换,具体请参考 performWork 。

更新

更新setState 通常要使用派发事件并调用interactiveUpdates$1(事件派发中的)。

  1. function interactiveUpdates$1(fn, a, b) {
  2. if (!isBatchingUpdates && !isRendering &&
  3. lowestPriorityPendingInteractiveExpirationTime !== NoWork) {
  4. // Synchronously flush pending interactive updates.
  5. performWork(lowestPriorityPendingInteractiveExpirationTime, false);
  6. lowestPriorityPendingInteractiveExpirationTime = NoWork;
  7. }
  8. var previousIsBatchingUpdates = isBatchingUpdates;
  9. isBatchingUpdates = true;
  10. try {
  11. // 执行事件的方法
  12. return scheduler.unstable_runWithPriority(scheduler.unstable_UserBlockingPriority,
  13. function () { return fn(a, b); });
  14. } finally {
  15. isBatchingUpdates = previousIsBatchingUpdates;
  16. if (!isBatchingUpdates && !isRendering) {
  17. performSyncWork();
  18. }
  19. }
  20. }

在 scheduler.unstable_runWithPriority 执行完事件后,就进入到 finally 中,此时 isBatchingUpdates 为false,
isRendering 为 false,直接执行 performSyncWork。

performSyncWork

Initial mount 和事件都是同步更新。在render中已接触过performSyncWork,但 render 和 更新略有不同。调用栈如下:
image.png

performWork

  1. function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
  2. findHighestPriorityRoot();// 查找是否有更新;获得nextFlushedRoot等值
  3. if (isYieldy) {
  4. {//...有 查找到 nextFlushedRoot 、nextFlushedExpirationTime
  5. performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
  6. findHighestPriorityRoot();
  7. }
  8. finishRendering();//没有更新,则结束渲染
  9. }

有点熟悉的味道,performWorkOnRoot 中执行的和之前的基本相同。

  1. function performWorkOnRoot(root, expirationTime, isYieldy) {
  2. isRendering = true; // Check if this is async work or sync/expired work.
  3. if (!isYieldy) {
  4. var finishedWork = root.finishedWork;
  5. if (finishedWork !== null) {
  6. // This root is already complete. We can commit it.
  7. completeRoot(root, finishedWork, expirationTime);
  8. } else {
  9. root.finishedWork = null;
  10. var timeoutHandle = root.timeoutHandle;
  11. if (timeoutHandle !== noTimeout) {
  12. root.timeoutHandle = noTimeout;
  13. cancelTimeout(timeoutHandle);
  14. }
  15. renderRoot(root, isYieldy);//渲染root
  16. finishedWork = root.finishedWork;
  17. if (finishedWork !== null) {
  18. // We've completed the root. Commit it.
  19. completeRoot(root, finishedWork, expirationTime);
  20. }
  21. }
  22. } else {//...}
  23. isRendering = false;
  24. }

更新时会从ReactRoot开始,创建整棵React Tree树,在渲染的过程中复用fiber。

renderRoot

  1. function renderRoot(root: FiberRoot, isYieldy: boolean): void {
  2. flushPassiveEffects();
  3. isWorking = true;
  4. if (
  5. expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null
  6. ) {
  7. // Reset the stack and start working from the root.
  8. resetStack();
  9. nextRoot = root;
  10. nextRenderExpirationTime = expirationTime;
  11. //更新,也是走此处,注意此时 nextUnitOfWork 被赋值
  12. nextUnitOfWork = createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime);
  13. root.pendingCommitExpirationTime = NoWork;
  14. }
  15. var didFatal = false;
  16. startWorkLoopTimer(nextUnitOfWork); //开始 "(React Tree Reconciliation)"
  17. do {
  18. try { workLoop(isYieldy); } catch (thrownValue) {//异常处理,此处省略 }
  19. break;
  20. } while (true);
  21. // We're done performing work. Time to clean up.
  22. isWorking = false;
  23. ReactCurrentDispatcher.current = null;
  24. resetContextDependences();
  25. resetHooks();
  26. // We completed the whole tree.
  27. const didCompleteRoot = true;
  28. stopWorkLoopTimer(interruptedBy, didCompleteRoot);// 删除 "(React Tree Reconciliation)"
  29. //....
  30. // Ready to commit.
  31. onComplete(root, rootWorkInProgress, expirationTime);
  32. }

createWorkInProgress

在更新中,这个是我们接触较多的方法。第一次执行时 current.alternate 被赋值为 workInProgress。

  1. function createWorkInProgress(current, pendingProps, expirationTime) {
  2. var workInProgress = current.alternate;
  3. if (workInProgress === null) {
  4. workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
  5. workInProgress.elementType = current.elementType;
  6. workInProgress.type = current.type;
  7. workInProgress.stateNode = current.stateNode;
  8. workInProgress.alternate = current;
  9. current.alternate = workInProgress;//一直保存 work.
  10. } else {
  11. workInProgress.pendingProps = pendingProps; // We already have an alternate.
  12. // Reset the effect tag.
  13. workInProgress.effectTag = NoEffect; // The effect list is no longer valid.
  14. workInProgress.nextEffect = null;
  15. workInProgress.firstEffect = null;
  16. workInProgress.lastEffect = null;
  17. if (enableProfilerTimer) {
  18. workInProgress.actualDuration = 0;
  19. workInProgress.actualStartTime = -1;
  20. }
  21. }
  22. workInProgress.childExpirationTime = current.childExpirationTime;
  23. workInProgress.expirationTime = current.expirationTime;//此时是0
  24. workInProgress.child = current.child;
  25. workInProgress.memoizedProps = current.memoizedProps;//当前的props
  26. workInProgress.memoizedState = current.memoizedState;//当前的state
  27. workInProgress.updateQueue = current.updateQueue;// 当前的更新队列。setState时改的就是fiber队列
  28. workInProgress.contextDependencies = current.contextDependencies;
  29. workInProgress.sibling = current.sibling;
  30. workInProgress.index = current.index;
  31. workInProgress.ref = current.ref;
  32. if (enableProfilerTimer) {
  33. workInProgress.selfBaseDuration = current.selfBaseDuration;
  34. workInProgress.treeBaseDuration = current.treeBaseDuration;
  35. }
  36. return workInProgress;
  37. }

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 并不是最新值。

  1. var classComponentUpdater = {
  2. isMounted: isMounted,
  3. enqueueSetState: function enqueueSetState(inst, payload, callback) {
  4. var fiber = get(inst);//此实例 inst 的fiber,指向上面的 || 标识的树。
  5. var currentTime = requestCurrentTime();
  6. var expirationTime = computeExpirationForFiber(currentTime, fiber);
  7. var update = createUpdate(expirationTime);
  8. update.payload = payload;
  9. if (callback !== undefined && callback !== null) {
  10. update.callback = callback;
  11. }
  12. flushPassiveEffects();
  13. enqueueUpdate(fiber, update);
  14. scheduleWork(fiber, expirationTime);
  15. }
  16. }

假如有以下示例:

  1. class App extends Component {
  2. state={
  3. count: 0
  4. }
  5. onClickFn = ()=>{
  6. this.setState({ count: this.state.count + 1 })
  7. }
  8. render() {
  9. return (<div id="appId">
  10. <span onClick={ this.onClickFn }点击 { this.state.count }</span>
  11. </div>)
  12. }
  13. }
  14. 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。由于值是对象类型,且是相互复用,所以一个上面改,两个都变。

  1. function performWorkOnRoot(root, expirationTime, isYieldy) {
  2. //...
  3. renderRoot(root, isYieldy);
  4. finishedWork = root.finishedWork;
  5. if (finishedWork !== null) {
  6. completeRoot(root, finishedWork, expirationTime);
  7. }
  8. //...
  9. }
  10. function renderRoot(){
  11. //...
  12. var rootWorkInProgress = root.current.alternate;//此处为交换点
  13. //...
  14. onComplete(root, rootWorkInProgress, expirationTime);
  15. }
  16. function onComplete(root, finishedWork, expirationTime) {
  17. root.pendingCommitExpirationTime = expirationTime;
  18. root.finishedWork = finishedWork;
  19. }
  20. function completeRoot(root, finishedWork, expirationTime) {
  21. //...
  22. root.finishedWork = null; //此处清空
  23. //...
  24. commitRoot(root, finishedWork);
  25. }
  26. function commitRoot(){
  27. //...省略代码
  28. root.current = finishedWork; //root为 FiberRoot
  29. //...
  30. }

beginWork

此时 renderExpirationTime是 1073741823,workInProgress.expirationTime 是0;current$$1是fiber;

  1. function beginWork(
  2. current: Fiber | null, workInProgress: Fiber, renderExpirationTime: ExpirationTime
  3. ): Fiber | null {
  4. const updateExpirationTime = workInProgress.expirationTime;
  5. if (current !== null) {
  6. const oldProps = current.memoizedProps;
  7. const newProps = workInProgress.pendingProps;
  8. if (oldProps !== newProps || hasLegacyContextChanged()) {
  9. didReceiveUpdate = true;
  10. } else if (updateExpirationTime < renderExpirationTime) {
  11. didReceiveUpdate = false;
  12. switch (workInProgress.tag) {
  13. case HostRoot:
  14. pushHostRootContext(workInProgress);
  15. resetHydrationState();
  16. break;
  17. case HostComponent:
  18. pushHostContext(workInProgress);
  19. break;
  20. case ClassComponent: {
  21. const Component = workInProgress.type;
  22. if (isLegacyContextProvider(Component)) {
  23. pushLegacyContextProvider(workInProgress);
  24. }
  25. break;
  26. }
  27. }
  28. // 更新执行此处
  29. return bailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime);
  30. }
  31. }
  32. }

在beginWork中会复用之前已完成的work,而不是重新创建fiber树。

  1. function bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime) {
  2. cancelWorkTimer(workInProgress);
  3. if (current$$1 !== null) {
  4. workInProgress.contextDependencies = current$$1.contextDependencies;
  5. }
  6. if (enableProfilerTimer) {
  7. stopProfilerTimerIfRunning(workInProgress);
  8. }
  9. var childExpirationTime = workInProgress.childExpirationTime;
  10. if (childExpirationTime < renderExpirationTime) {
  11. return null;
  12. } else {
  13. cloneChildFibers(current$$1, workInProgress);//clone子项
  14. return workInProgress.child;//返回第一项
  15. }
  16. }
  17. //clone子fiber
  18. function cloneChildFibers(current$$1, workInProgress) {
  19. if (workInProgress.child === null) { return; }
  20. var currentChild = workInProgress.child;//拿到子fiber
  21. //重点,复用的是currentChild.pendingProps,这就导致下次循环中oldProps === newProps 的出现
  22. var newChild = createWorkInProgress(currentChild, currentChild.pendingProps,
  23. currentChild.expirationTime);
  24. workInProgress.child = newChild;
  25. newChild.return = workInProgress;
  26. while (currentChild.sibling !== null) {// 修改兄弟节点
  27. currentChild = currentChild.sibling;
  28. //复制兄弟节点,也是 pendingProps
  29. newChild = newChild.sibling = createWorkInProgress(currentChild,
  30. currentChild.pendingProps,
  31. currentChild.expirationTime);
  32. newChild.return = workInProgress;
  33. }
  34. newChild.sibling = null;//经过上面的循环,newChild已执行最后的兄弟节点
  35. }

此时克隆的是ReactRoot的子项。

updateClassComponent

  1. function updateClassComponent(current$$1, workInProgress,
  2. Component, nextProps, renderExpirationTime) {
  3. //...省略
  4. var instance = workInProgress.stateNode;
  5. var shouldUpdate = void 0;
  6. if (instance === null) {
  7. //...省略
  8. } else if (current$$1 === null) {
  9. //...省略
  10. }else{
  11. shouldUpdate = updateClassInstance(current$$1, workInProgress,
  12. Component, nextProps, renderExpirationTime);
  13. }
  14. var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component,
  15. shouldUpdate, hasContext, renderExpirationTime);
  16. //...省略
  17. return nextUnitOfWork;
  18. }

在updateClassInstance 中计算 state,执行componentWillUpdate、getDerivedStateFromProps或componentWillReceiveProps。标记componentDidUpdate、getSnapshotBeforeUpdate。

  1. function updateClassInstance(current, workInProgress, ctor, newProps, renderExpirationTime) {
  2. var instance = workInProgress.stateNode;
  3. var oldProps = workInProgress.memoizedProps;
  4. instance.props = workInProgress.type === workInProgress.elementType ? oldProps :
  5. resolveDefaultProps(workInProgress.type, oldProps);
  6. var oldContext = instance.context;
  7. var contextType = ctor.contextType;
  8. var nextContext = void 0;
  9. if (typeof contextType === 'object' && contextType !== null) {
  10. nextContext = _readContext(contextType);
  11. } else {
  12. var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
  13. nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
  14. }
  15. var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
  16. var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' ||
  17. typeof instance.getSnapshotBeforeUpdate === 'function';
  18. // 新api getDerivedStateFromProps 和 componentWillReceiveProps 冲突
  19. if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
  20. typeof instance.componentWillReceiveProps === 'function')) {
  21. if (oldProps !== newProps || oldContext !== nextContext) {
  22. callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
  23. }
  24. }
  25. resetHasForceUpdateBeforeProcessing();
  26. var oldState = workInProgress.memoizedState;
  27. var newState = instance.state = oldState;
  28. var updateQueue = workInProgress.updateQueue;//当前对象上的更新队列
  29. if (updateQueue !== null) {
  30. processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
  31. newState = workInProgress.memoizedState;//newState 诞生
  32. }
  33. if (oldProps === newProps && oldState === newState && !hasContextChanged()
  34. && !checkHasForceUpdateAfterProcessing()) {//通常更新不符合此条件
  35. if (typeof instance.componentDidUpdate === 'function') {
  36. if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
  37. workInProgress.effectTag |= Update;
  38. }
  39. }
  40. if (typeof instance.getSnapshotBeforeUpdate === 'function') {
  41. if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
  42. workInProgress.effectTag |= Snapshot;
  43. }
  44. }
  45. return false;
  46. }
  47. if (typeof getDerivedStateFromProps === 'function') {//执行 getDerivedStateFromProps
  48. applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
  49. newState = workInProgress.memoizedState;
  50. }
  51. var shouldUpdate = checkHasForceUpdateAfterProcessing() ||
  52. checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps,
  53. oldState, newState, nextContext);
  54. if (shouldUpdate) {// 返回true和 false,有助于更新
  55. if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function'
  56. || typeof instance.componentWillUpdate === 'function')) {
  57. startPhaseTimer(workInProgress, 'componentWillUpdate');
  58. if (typeof instance.componentWillUpdate === 'function') {
  59. instance.componentWillUpdate(newProps, newState, nextContext);
  60. }
  61. if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
  62. instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
  63. }
  64. stopPhaseTimer();
  65. }
  66. if (typeof instance.componentDidUpdate === 'function') {//标记 Update
  67. workInProgress.effectTag |= Update;
  68. }
  69. if (typeof instance.getSnapshotBeforeUpdate === 'function') {//标记 Snapshot
  70. workInProgress.effectTag |= Snapshot;
  71. }
  72. } else {
  73. // 如果 shouldComponentUpdate 返回false。
  74. // 我们还应该更新memoized的props/state,以表明该工作可以重用
  75. if (typeof instance.componentDidUpdate === 'function') {
  76. if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {//条件
  77. workInProgress.effectTag |= Update;
  78. }
  79. }
  80. if (typeof instance.getSnapshotBeforeUpdate === 'function') {
  81. if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {//条件
  82. workInProgress.effectTag |= Snapshot;
  83. }
  84. }
  85. workInProgress.memoizedProps = newProps;
  86. workInProgress.memoizedState = newState;
  87. }
  88. instance.props = newProps;// 更新实例的props
  89. instance.state = newState;// 更新实例的state
  90. instance.context = nextContext;// 更新context
  91. return shouldUpdate;
  92. }

在processUpdateQueue计算newState。 newProps是在执行组件render方法时得到。执行完此方法就更新了state、porps、context。
接着执行updateClassComponent 中 finishClassComponent。在finishClassComponent 中调用 instance.render()生成 nextChildren。之后与render时一样,调用reconcileChildren,调用栈如下:
image.png
调用与render 无异,只是在 reconcileChildren 中 current$$1.child有值,可以使用之前的fiber,最后也是使用createWorkInProgress 克隆fiber。

在 createWorkInProgress 中 current.alternate 为 nulll 会创建 fiber。第一次更新会为当前 fiber 的 alternate 赋值。

updateHostComponent

和render的updateHostComponent基本一致。beginWork的updateHostComponent 主要用于生成子 fiber。

  1. function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
  2. pushHostContext(workInProgress);
  3. if (current$$1 === null) {
  4. tryToClaimNextHydratableInstance(workInProgress);
  5. }
  6. var type = workInProgress.type;
  7. var nextProps = workInProgress.pendingProps;
  8. var prevProps = current$$1 !== null ? current$$1.memoizedProps : null;
  9. var nextChildren = nextProps.children;
  10. var isDirectTextChild = shouldSetTextContent(type, nextProps);
  11. if (isDirectTextChild) {
  12. nextChildren = null;
  13. } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
  14. workInProgress.effectTag |= ContentReset;
  15. }
  16. markRef(current$$1, workInProgress);
  17. if (renderExpirationTime !== Never && workInProgress.mode & ConcurrentMode
  18. && shouldDeprioritizeSubtree(type, nextProps)) {
  19. workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
  20. return null;
  21. }
  22. reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
  23. return workInProgress.child;
  24. }

updateHostText

  1. function updateHostText(current, workInProgress) {
  2. if (current === null) {
  3. tryToClaimNextHydratableInstance(workInProgress);
  4. }
  5. return null;
  6. }

completeUnitOfWork

beginWork中 执行组件中的生命周期,并为组件标记effectTag。completeUnitOfWork 中则为HostText、HostComponent 标记effectTag。

  1. function completeUnitOfWork(workInProgress) {
  2. while (true) {
  3. var current$$1 = workInProgress.alternate;
  4. var returnFiber = workInProgress.return;
  5. var siblingFiber = workInProgress.sibling;
  6. //...
  7. nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
  8. //...
  9. //... effect 链表
  10. //...省略
  11. }
  12. return null;
  13. }
  14. function completeWork(current, workInProgress, renderExpirationTime) {
  15. var newProps = workInProgress.pendingProps;
  16. switch (workInProgress.tag) {
  17. //...
  18. case HostText:{
  19. var newText = newProps;
  20. if (current && workInProgress.stateNode != null) {
  21. var oldText = current.memoizedProps;
  22. updateHostText$1(current, workInProgress, oldText, newText);
  23. } else {
  24. //创建text
  25. }
  26. break;
  27. }
  28. case HostComponent:{
  29. popHostContext(workInProgress);
  30. var rootContainerInstance = getRootHostContainer();
  31. var type = workInProgress.type;
  32. if (current !== null && workInProgress.stateNode != null) {
  33. updateHostComponent$1(current, workInProgress, type, newProps,
  34. rootContainerInstance);
  35. if (current.ref !== workInProgress.ref) {
  36. markRef$1(workInProgress);
  37. }
  38. } else { //省略 创建 host Componennt }
  39. break;
  40. }
  41. }
  42. //....
  43. }

completeWork 解析的就是 HostComponent、HostText。

updateHostComponent$1

updateHostComponent主要用于标记 dom 属性的变化,返回更新队列。

  1. updateHostComponent$1 = function updateHostComponent$1(current, workInProgress, type,
  2. newProps, rootContainerInstance) {
  3. var oldProps = current.memoizedProps;
  4. if (oldProps === newProps) { return; }
  5. var instance = workInProgress.stateNode;
  6. var currentHostContext = getHostContext();
  7. var updatePayload = prepareUpdate(instance, type, oldProps, newProps,
  8. rootContainerInstance, currentHostContext);
  9. workInProgress.updateQueue = updatePayload;
  10. if (updatePayload) {
  11. markUpdate(workInProgress);
  12. }
  13. };
  14. function prepareUpdate(domElement, type, oldProps,
  15. newProps, rootContainerInstance, hostContext) {
  16. return diffProperties(domElement, type, oldProps, newProps, rootContainerInstance);
  17. }
  18. //计算属性 packages\react-dom\src\client\ReactDOMComponent.js
  19. function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {
  20. // 对比STYLE$1
  21. // 对比更新的属性
  22. return updatePayload
  23. }

updateHostText$1

updateHostText 用于标记文本的更新。

  1. //文本更新
  2. updateHostText$1 = function updateHostText$1(current, workInProgress, oldText, newText) {
  3. if (oldText !== newText) { markUpdate(workInProgress);//标记有更新 }
  4. }
  5. function markUpdate(workInProgress) {
  6. workInProgress.effectTag |= Update;
  7. }

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,基本是忽略。