在执行一些操作时,若需要遍历整棵dom树后才能更新,这就很低效了!若dom 树中有更新,React会给当前fiber添加effectTag,以标记变更。

effectTags

  1. //packages\shared\ReactSideEffectTags.js
  2. export type SideEffectTag = number;
  3. // Don't change these two values. They're used by React Dev Tools.
  4. export const NoEffect = /* */ 0b000000000000;
  5. export const PerformedWork = /* */ 0b000000000001;
  6. // You can change the rest (and add more).
  7. export const Placement = /* */ 0b000000000010;
  8. export const Update = /* */ 0b000000000100;
  9. export const PlacementAndUpdate = /* */ 0b000000000110;
  10. export const Deletion = /* */ 0b000000001000;
  11. export const ContentReset = /* */ 0b000000010000;
  12. export const Callback = /* */ 0b000000100000;
  13. export const DidCapture = /* */ 0b000001000000;
  14. export const Ref = /* */ 0b000010000000;
  15. export const Snapshot = /* */ 0b000100000000;
  16. export const Passive = /* */ 0b001000000000;
  17. // Passive & Update & Callback & Ref & Snapshot
  18. export const LifecycleEffectMask = /* */ 0b001110100100;
  19. // Union of all host effects
  20. export const HostEffectMask = /* */ 0b001111111111;
  21. export const Incomplete = /* */ 0b010000000000;
  22. export const ShouldCapture = /* */ 0b100000000000;

effectTag 的值是二进制,使用&、| 可以很方便计算出需要的操作,例如,Placement | Update = PlacementAndUpdate。

effect

在ReactFiber中对effect进行了注释:

  1. // Effect
  2. effectTag: SideEffectTag,
  3. // Singly linked list fast path to the next fiber with side-effects.
  4. nextEffect: Fiber | null,
  5. // The first and last fiber with side-effect within this subtree.
  6. // This allows us to reuse a slice of the linked list when we reuse the work
  7. // done within this fiber.
  8. firstEffect: Fiber | null,
  9. lastEffect: Fiber | null

effect添加

fiber是增量的。在workloop中会逐渐生成fiber,在workLoop中fiber是workInProgress。例如,在创建一个组件fiber,实例有componentDidUpdate时,则添加一个Update effect。

  1. //ReactFiberClassComponent line:1057
  2. if (typeof instance.componentDidUpdate === 'function') {
  3. if (oldProps !== current.memoizedProps || oldState !== current.memoizedState ) {
  4. workInProgress.effectTag |= Update;
  5. }
  6. }
  7. if (typeof instance.getSnapshotBeforeUpdate === 'function') {
  8. if ( oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
  9. workInProgress.effectTag |= Snapshot;
  10. }
  11. }

effect 链表

workloop的循环是不停的执行 performUnitOfWork。若当前的fiber没有子项时next=null则完成(complete)当前工作。

  1. function performUnitOfWork(workInProgress){
  2. if (next === null) {
  3. // If this doesn't spawn new work, complete the current work.
  4. next = completeUnitOfWork(workInProgress);
  5. }
  6. }

在complete the current work时,会记一个 effect 的链表。

  1. //在completeUnitOfWork
  2. function completeUnitOfWork(workInProgress){
  3. //...省略代码
  4. if( workInProgress.effectTag & Incomplete) === NoEffect ){
  5. if (
  6. returnFiber !== null &&
  7. // 如果兄弟姐妹没有完成,不要给父母添加效果
  8. (returnFiber.effectTag & Incomplete) === NoEffect
  9. ) {
  10. if (returnFiber.firstEffect === null) {//链表第一项一直透传
  11. returnFiber.firstEffect = workInProgress.firstEffect;
  12. }
  13. if (workInProgress.lastEffect !== null) {
  14. if (returnFiber.lastEffect !== null) {
  15. returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
  16. }
  17. returnFiber.lastEffect = workInProgress.lastEffect;
  18. }
  19. const effectTag = workInProgress.effectTag;
  20. if (effectTag > PerformedWork) {
  21. if (returnFiber.lastEffect !== null) {
  22. returnFiber.lastEffect.nextEffect = workInProgress;
  23. } else {
  24. returnFiber.firstEffect = workInProgress;
  25. }
  26. returnFiber.lastEffect = workInProgress;
  27. }
  28. }
  29. }
  30. //...省略代码
  31. }

链表例子

例如,假如有3个effect,A、B、C有effect:
root.fiber
A

B C D

第一次B有effect时:

A.firstEffect = BworkInProgress;
A.lastEffect = BworkInProgress;

第二次C有effect时:
A.lastEffect.nextEffect = BworkInProgress.nextEffect = CworkInProgress;
A.lastEffect = CworkInProgress;

第三次D无effect时:
不操作

第四次A有effect时:
root.fiber.firstEffect = A.firstEffect = BworkInProgress;
root.fiber.lastEffect= CworkInProgress;

CworkInProgress.nextEffect = AworkInProgress;
root.fiber.lastEffect = AworkInProgress;

第五次由于returnFiber = null,所以不做处理。

此次的结果为:

层级 firstEffect lastEffect nextEffect
root BworkInProgress AworkInProgress null
A BworkInProgress CworkInProgress
B null null CworkInProgress
C null nul AworkInProgress
D null null nul

commitRoot

workLoop中生成effect的链表,在commitRoot 时会用到链表做一些操作:
commitBeforeMutationLifecycles 会提交 hook 和 getSnapshotBeforeUpdate;
commitAllHostEffects 提交fibers到浏览器,执行完提交就可以在浏览器看到元素;
commitAllLifeCycles 提交组件的生命周期,componentDidMount 和 componentDidUpdate等在挂载后的effect;

  1. function commitRoot(root,finishedWork){
  2. let firstEffect;//找到链表的第一项
  3. if (finishedWork.effectTag > PerformedWork) {
  4. if (finishedWork.lastEffect !== null) {
  5. finishedWork.lastEffect.nextEffect = finishedWork;
  6. firstEffect = finishedWork.firstEffect;
  7. } else {
  8. firstEffect = finishedWork;
  9. }
  10. } else {
  11. // There is no effect on the root.
  12. firstEffect = finishedWork.firstEffect;
  13. }
  14. //...省略代码
  15. nextEffect = firstEffect;
  16. startCommitSnapshotEffectsTimer();
  17. while (nextEffect !== null) {
  18. let didError = false;
  19. let error;
  20. try {
  21. commitBeforeMutationLifecycles();
  22. } catch (e) {
  23. didError = true;
  24. error = e;
  25. }
  26. }
  27. stopCommitSnapshotEffectsTimer();
  28. //之前的都是标记,此处做插入、删除、卸载
  29. nextEffect = firstEffect;
  30. startCommitHostEffectsTimer();
  31. while (nextEffect !== null) {
  32. let didError = false;
  33. let error;
  34. try {
  35. commitAllHostEffects();//此方法过后,可以看到dom元素
  36. } catch (e) {
  37. didError = true;
  38. error = e;
  39. }
  40. }
  41. stopCommitHostEffectsTimer();
  42. // placements, updates and deletions 在整个树中已经被调用
  43. nextEffect = firstEffect;
  44. startCommitLifeCyclesTimer();
  45. while (nextEffect !== null) {
  46. let didError = false;
  47. let error;
  48. try {
  49. commitAllLifeCycles(root, committedExpirationTime);//所有的声明周期
  50. } catch (e) {
  51. didError = true;
  52. error = e;
  53. }
  54. }
  55. isCommitting = false;
  56. isWorking = false;
  57. stopCommitLifeCyclesTimer();
  58. }

链表的应用

在commitAllHostEffects中是一个大的while循环,在循环中nextEffect。例如上面例子的执行的方式是:
nextEffect -> BworkInProgress,nextEffect = nextEffect.nextEffect;
nextEffect -> CworkInProgress,nextEffect = nextEffect.nextEffect;
nextEffect-> AworkInProgress,nextEffect = nextEffect.nextEffect;
nextEffect -> null,结束循环;

  1. function commitAllHostEffects() {
  2. while (nextEffect !== null) {
  3. recordEffect();
  4. const effectTag = nextEffect.effectTag;
  5. if (effectTag & ContentReset) {
  6. commitResetTextContent(nextEffect);
  7. }
  8. if (effectTag & Ref) {
  9. const current = nextEffect.alternate;
  10. if (current !== null) {
  11. commitDetachRef(current);
  12. }
  13. }
  14. let primaryEffectTag = effectTag & (Placement | Update | Deletion);
  15. switch (primaryEffectTag) {
  16. case Placement: {
  17. commitPlacement(nextEffect);
  18. nextEffect.effectTag &= ~Placement;
  19. break;
  20. }
  21. case PlacementAndUpdate: {
  22. commitPlacement(nextEffect);
  23. nextEffect.effectTag &= ~Placement;
  24. const current = nextEffect.alternate;
  25. commitWork(current, nextEffect);
  26. break;
  27. }
  28. case Update: {
  29. const current = nextEffect.alternate;
  30. commitWork(current, nextEffect);
  31. break;
  32. }
  33. case Deletion: {
  34. commitDeletion(nextEffect);
  35. break;
  36. }
  37. }
  38. nextEffect = nextEffect.nextEffect;
  39. }
  40. }