Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react解析: render的中的update(四) #14

Open
HerryLo opened this issue Oct 22, 2019 · 0 comments
Open

react解析: render的中的update(四) #14

HerryLo opened this issue Oct 22, 2019 · 0 comments
Assignees
Labels

Comments

@HerryLo
Copy link
Member

HerryLo commented Oct 22, 2019

感谢 yck: 剖析 React 源码解析,本篇文章是在读完他的文章的基础上,将他的文章进行拆解和加工,加入我自己的一下理解和例子,便于大家理解。觉得yck写的真的很棒 。React 版本为 16.8.6,关于源码的阅读,可以移步到yck react源码解析

本文永久有效链接: react解析: render的中的update(四)

上一章节说到,不存在root数据节点,即通过createFiberRoot 函数创建FiberRootFiberRoot对象是整个React应用的起点,同时也记录了整个React应用更新过程中的各种信息。

下面将要聊到的就是,当root呗创建后,还会发生什么👇👇

legacyRenderSubtreeIntoContainer 函数

下面衔接上一部分内容,不懂得可以查看上一章节

yck: ReactDOM 源码 554行 legacyRenderSubtreeIntoContainer

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean,
  callback: ?Function,
) {
    // 初始化时,container 肯定没有 _reactRootContainer属性
    let root: Root = (container._reactRootContainer: any);
    if (!root) {
        // 省略创建root部分
        
        unbatchedUpdates(() => {
            if (parentComponent != null) {
                root.legacy_renderSubtreeIntoContainer(
                    parentComponent,
                    children,
                    callback,
                );
            } else {
                root.render(children, callback);
            }
        });
    }
}

在root刚刚被创建时,parentComponent一般都为null;

unbatchedUpdates函数在这里作用是:告知React内部不进行批量更新,即不用将多个setState合并为一个;
setState在后面的章节我们将会说到

那么这里实际调用的就是root.render函数,root是ReactRoot实例对象,即调用 root.render函数 == ReactRoot.prototype.render函数

ReactRoot.prototype.render 函数

yck: ReactRoot 源码 377行 ReactRoot.prototype.render

ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  // 这里指 FiberRoot
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;

  // 如果有 callback,就 push 进 work 中的数组
  if (callback !== null) {
    work.then(callback);
  }
  // work._onCommit 就是用于执行所有回调函数的
  updateContainer(children, root, null, work._onCommit);
  return work;
};

函数中的参数children即是ReactElement节点对象,callback为回调函数。ReactWork实例对象的主要作用就是维护一个回调数组,可查看yck: ReactWork 源码 327行,如果传入参数中存在callback,就将其挂载ReactWork实例对象中;

下面来看看updateContainer函数会做什么。

updateContainer 函数

yck: ReactFiberReconciler 源码 284行 updateContainer

export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): ExpirationTime {
  const current = container.current;
  // 计算时间
  const currentTime = requestCurrentTime();
  // expirationTime 代表优先级,数字越大优先级越高
  // sync 的数字是最大的,所以优先级也是最高的
  const expirationTime = computeExpirationForFiber(currentTime, current);
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}

container.current即是从FiberRoot中取出RootFiber对象,currentTime就是当前距离React应用初始化的时间。 **expirationTime字面意思就是过期时间,后面我会专门花一章的时间来介绍这两个时间,这两个时间也是React应用任务调度的重点。

scheduleRootUpdate函数

updateContainerAtExpirationTime函数实际调用的就是scheduleRootUpdate函数,下面来说一下scheduleRootUpdate函数的作用。

yck: ReactFiberReconciler 源码 114行 scheduleRootUpdate

function scheduleRootUpdate(
  current: Fiber,
  element: ReactNodeList,
  expirationTime: ExpirationTime,
  callback: ?Function,
) {
  // 创建一个 update,就是内部有几个属性的对象
  const update = createUpdate(expirationTime);
  update.payload = {element};

  // render中的回调函数 
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    update.callback = callback;
  }

  flushPassiveEffects();
  // 把 update 入队,内部就是一些创建或者获取 queue(链表结构),然后给链表添加一个节点的操作
  enqueueUpdate(current, update);
  scheduleWork(current, expirationTime);

  return expirationTime;
}

下面就是update对象其中的属性:

// update对象属性
export type Update<State> = {
  // 更新的过期时间
  expirationTime: ExpirationTime,

  // export const UpdateState = 0;
  // export const ReplaceState = 1;
  // export const ForceUpdate = 2;
  // export const CaptureUpdate = 3;
  // 指定更新的类型,值为以上几种
  tag: 0 | 1 | 2 | 3,
  // 更新内容,比如`setState`接收的第一个参数
  payload: any,
  // 对应的回调,`setState`,`render`都有
  callback: (() => mixed) | null,

  // 指向下一个更新
  next: Update<State> | null,
  // 指向下一个`side effect`
  nextEffect: Update<State> | null,
};

udate对象会被插入到React应用维护的任务队列中,不管你是setState还是ReactDOM.render造成的 React应用 更新都是如此。这个函数核心作用就是创建或者获取一个队列,然后把 update 对象插入队列进行更新。scheduleWork函数就是任务调度的东西了。

更多内容:

react解析: React.createElement(一)

react解析: React.Children(二)

react解析: render的FiberRoot(三)

参考:

yck: 剖剖析 React 源码

Jokcy 的 《React 源码解析》: react.jokcy.me/

ps: 顺便推一下自己的个人公众号:Yopai,有兴趣的可以关注,每周不定期更新,分享可以增加世界的快乐

@HerryLo HerryLo self-assigned this Oct 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant