React源码解析3-reactFiber(1)

336 阅读4分钟

1.概念

整体的更新流程引入fiber后拆分成啦以fiber为结构的跟新流程,且我们可以按优先级更新和中断。

主要是ReactFiberScheduler.js文件这里面有很多公共变量 我们对这些变量的理解和他们再别处被调用修改的值的理解是关键。

大概流程:

image-20210423164117770

总结:保证我们低优先级的跟新不会影响浏览器的动画等渲染

2.schedulework

1.找到更新对应的fiberRoot节点 因为我们setState和forceupdate时候传过去的fiber都是对应的组件

2.如果符合一定条件就重置stack 立马存储一些重要变量

3.如果符合条件就请求工作调度

image-20210423164954404

ps:我们Button点击 是list组件调用setState 但是是我们的rootFiber加入调度队列中的,加入调度的都是root节点 因为跟新的开始都是root节点

3.batchupdates

//requestwork.js 
if (isBatchingUpdates) {//!批处理相关知识
    // Flush work at the end of the batch.
    if (isUnbatchingUpdates) {
      // ...unless we're inside unbatchedUpdates, in which case we should
      // flush it now.
      nextFlushedRoot = root;
      nextFlushedExpirationTime = Sync;
      performWorkOnRoot(root, Sync, true);
    }
    return; //如果是批量更新直接return 不执行下面的performSyncWork,scheduleCallbackWithExpirationTime
  }

  // TODO: Get rid of Sync and use current time?
  //!更具expirationTime的类型去调用相关调度方法
  if (expirationTime === Sync) {
    performSyncWork();//!同步 无法打断
  } else {
    scheduleCallbackWithExpirationTime(root, expirationTime);//!异步
  }

让我们isBatchingUpdates = true 然后再requestwork里面直接return 不去执行任务,然后再最后一起执行sync的更新fiber的updateQueue一次执行。

所有的事件回调函数都会走batchupdates逻辑

function batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
  const previousIsBatchingUpdates = isBatchingUpdates;
  isBatchingUpdates = true;
  try {
    return fn(a);//!执行事件的回调函数 等里面所有setState都加入到udpateQueue中
  } finally {
    //!判断当前是否要batchUpdates
    isBatchingUpdates = previousIsBatchingUpdates;
    if (!isBatchingUpdates && !isRendering) {
      performSyncWork();//!同步方式执行 代替requestWork里面执行更新
    }
  }
}

普通节点渲染 调用unbatchedUpdates isBatchingUpdates为false 然后update走到fiber的queue里面 最后再执行requestwork 这时候isBatchingUpdates为false 就去分情况任务调度

//!批量更新
    DOMRenderer.unbatchedUpdates(() => {
      if (parentComponent != null) {
        root.legacy_renderSubtreeIntoContainer(
          parentComponent,
          children,
          callback,
        );
      } else {
        //!调用render方法 root是ReactRoot对象
        root.render(children, callback);//!children是渲染到container的孩子节点
      }
    });
//unbatchedUpdates
function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
  if (isBatchingUpdates && !isUnbatchingUpdates) {
    isUnbatchingUpdates = true;
    try {
      return fn(a);
    } finally {
      isUnbatchingUpdates = false;
    }
  }
  return fn(a);
}

isbatching只有再事件回调函数或者自己调用batchupdate时候 开始会把isbatching为true 任务结束后变为false 所以其他地方比如(dom第一次渲染)就是false

事件相关的是再interactiveUpdates$1函数

setTimeout(() => {
       this.countNumber()
     }, 0)

这种情况下整个函数是在fn里面执行 这时候只是读取了settimeout 还没执行里面回调 就退出了batchupdates(相当于只执行啦读取setTimeout 但是里面的函数包括setState都是异步执行 那后面触发setTimeout就不会走batchupdates,而是直接走requestWork) 但是setTimeout的回调函数是后面自己执行 这时候就会直接去requestWork 那么此时batchupdates为false 就会去走同步任务刷新视图

setTimeout(() => {
      batchedUpdates(() => this.countNumber())
    }, 0) 

这个代码当回掉执行的时候 不是直接去requestWork 而是再走batchedUpdates去设置全局batchUodates为true 这时候又是直接return 在finaly里面走performSync

        isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
        isBatchingUpdates = previousIsBatchingUpdates;

        if (!isBatchingUpdates && !isRendering) {
          performSyncWork();
        }

这部分逻辑为什么取之前的previousIsBatchingInteractiveUpdates 因为就怕之前是true 那么就表示外层的还是在批量更新过程中 因为批量结束后一定是false. 如果还在更新过程中 我们就直接return 让他们在最外层的批量更新里去刷新所有的updateQueue队列。

记住直接的绑定事件有两层 1.intertive 2.batchupdates 在第二层时候调用prebatchupdates即外层的batchupdatese为true 所以会在最外层去performSyncWork

直接调用batchedUpdates 只有一层 这时候prebatchupdates即外层的batchupdatese为false 所以这一层去performSyncWork

setState第二个参数是以回调函数形式存放到update的callback中 在updateQueue执行完再去执行

**ps:**异步setState函数是自己进入requestWork的 事件都封装啦batch但是异步函数只会读取最外层的函数没执行里面的函数 所以里面执行的时候直接去requestWork就没batch啦

流程图

image-20210426092636300