React源码分析3-render阶段(穿插scheduler和reconciler)

2023-11-15

本章将讲解 react 的核心阶段之一 —— render阶段,我们将探究以下部分内容的源码:

  • 更新任务的触发
  • 更新任务的创建
  • reconciler 过程同步和异步遍历及执行任务
  • scheduler 是如何实现帧空闲时间调度任务以及中断任务的

触发更新

触发更新的方式主要有以下几种:ReactDOM.rendersetStateforUpdate 以及 hooks 中的 useState 等,关于 hooks 的我们后面再详细讲解,这里先关注前三种情况。

ReactDOM.render

ReactDOM.render 作为 react 应用程序的入口函数,在页面首次渲染时便会触发,页面 dom 的首次创建,也属于触发 react 更新的一种情况。

首先调用 legacyRenderSubtreeIntoContainer 函数,校验根节点 root 是否存在,若不存在,调用 legacyCreateRootFromDOMContainer 创建根节点 root、rootFiber 和 fiberRoot 并绑定它们之间的引用关系,然后调用 updateContainer 去非批量执行后面的更新流程;若存在,直接调用 updateContainer 去批量执行后面的更新流程:

// packages/react-dom/src/client/ReactDOMLegacy.js

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,  children: ReactNodeList,  container: Container,  forceHydrate: boolean,  callback: ?Function,
) {
   
  // ...
  let root: RootType = (container._reactRootContainer: any);
  let fiberRoot;
  if (!root) {
   
    // 首次渲染时根节点不存在
    // 通过 legacyCreateRootFromDOMContainer 创建根节点、fiberRoot 和 rootFiber
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
   
      const originalCallback = callback;
      callback = function() {
   
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // 非批量执行更新流程
    unbatchedUpdates(() => {
   
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
   
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
   
      const originalCallback = callback;
      callback = function() {
   
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // 批量执行更新流程
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}

updateContainer 函数中,主要做了以下几件事情:

  • requestEventTime:获取更新触发的时间
  • requestUpdateLane:获取当前任务优先级
  • createUpdate:创建更新
  • enqueueUpdate:将任务推进更新队列
  • scheduleUpdateOnFiber:调度更新
    关于这几个函数稍后会详细讲到
// packages/react-dom/src/client/ReactDOMLegacy.js

export function updateContainer(
  element: ReactNodeList,  container: OpaqueRoot,  parentComponent: ?React$Component<any, any>,  callback: ?Function,
): Lane {
   
  // ...
  const current = container.current;
  const eventTime = requestEventTime(); // 获取更新触发的时间
  // ...
  const lane = requestUpdateLane(current); // 获取任务优先级

  if (enableSchedulingProfiler) {
   
    markRenderScheduled(lane);
  }

  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
   
    container.context = context;
  } else {
   
    container.pendingContext = context;
  }

  // ...

  const update = createUpdate(eventTime, lane); // 创建更新任务
  update.payload = {
   element};

  callback = callback === undefined ? null : callback;
  if (callback !== null) {
   
    // ...
    update.callback = callback;
  }

  enqueueUpdate(current, update); // 将任务推入更新队列
  scheduleUpdateOnFiber(current, lane, eventTime); // schedule 进行调度

  return lane;
}

setState

setState 时类组件中我们最常用的修改状态的方法,状态修改会触发更新流程。

class 组件在原型链上定义了 setState 方法,其调用了触发器 updater 上的 enqueueSetState 方法:

// packages/react/src/ReactBaseClasses.js

Component.prototype.setState = function(partialState, callback) {
   
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',
  );
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

然后我们再来看以下 updater 上定义的 enqueueSetState 方法,一看到这我们就了然了,和 updateContainer 方法中做的事情几乎一模一样,都是触发后续的更新调度。

相关参考视频讲解:进入学习

// packages/react-reconciler/src/ReactFiberClassComponent.old.js

const classComponentUpdater = {
   
  isMounted,
  enqueueSetState(inst, payload, callback) {
   
    const fiber = getInstance(inst);
    const eventTime = requestEventTime(); // 获取更新触发的时间
    const lane = requestUpdateLane(fiber); // 获取任务优先级

    const update = createUpdate(eventTime, lane); // 创建更新任务
    update.payload = payload
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React源码分析3-render阶段(穿插scheduler和reconciler) 的相关文章

随机推荐

  • 架构设计之道【精】

    一 本质 在了解架构本质之前先了解下架构的定义 百度百科对架构的定义 架构又名软件架构 是有关软件整体结构与组件的抽象描述 用于指导大型软件系统各个方面的设计 从定义中我们提炼出几个关键字 组件 结构 关系 组件 也可以称为软件元素或者是架
  • vscode 扩展开发从入门到颈椎病康复

    笔者从业以来 各路插件开发无算 而 vscode 把插件开发体验做到了极致 其开发体验 如沐春风 如丝般顺滑 经常写完了还想删掉再写一遍 vscode 扩展的内置脚手架细心且精致 一键生成后即可运行 vscode 库类型完美 因此开发者可以
  • Sentinel + Gateway网关动态限流

    Sentinel 控制台 0 概述 Sentinel 控制台是流量控制 熔断降级规则统一配置和管理的入口 它为用户提供了机器自发现 簇点链路自发现 监控 规则配置等功能 在 Sentinel 控制台上 我们可以配置规则并实时查看流量控制效果
  • python爬虫要用到的库_Python写爬虫都用到什么库

    Python爬虫 全称Python网络爬虫 是一种按照一定的规则 自动地抓取万维网信息的程序或脚本 主要用于抓取证券交易数据 天气数据 网站用户数据和图片数据等 Python为支持网络爬虫正常功能实现 内置了大量的库 主要有几种类型 下面本
  • 音频处理-2 WAV格式

    后续要将流量中的音频数据转为WAV格式文件 所以本节重点说下WAV格式 WAV文件是在PC机平台上很常见的 最经典的多媒体音频文件 最早于1991年8月出现在Windows 3 1操作系统上 文件扩展名为WAV 是WaveFom的简写 也称
  • MarkDown超级教程 Obsidian版_11.4

    date 2021 11 3 18 01 aliases Markdown教程 MD教程 tags Markdown 什么是 Markdown Markdown 是一款轻量级标记语言 不同于HTML Hypertext Markup Lan
  • 修改notebook的默认路径_Anaconda3修改jupyter_notebook打开的默认路径

    1 windows下 找到jupyter notebook配置文件jupyter notebook config py 默认安装在 C Users Administrator jupyter jupyter notebook config
  • GB28181协议EasyGBS国标视频云平台无法正常启动的排查步骤与解决方法

    EasyGBS国标视频云服务是基于国标GB T28181协议的视频能力平台 可实现的视频功能包括 实时监控直播 录像 检索与回看 语音对讲 云存储 告警 平台级联等功能 平台部署简单 可拓展性强 支持将接入的视频流进行全终端 全平台分发 分
  • upload-labs

    pass 01 先发一个后缀名为PHP的文件 发现不能上传 然后禁用js 说明 js就是所谓的客户端脚本语言 是一种在互联网浏览器 浏览器也称为Web客户端 因为它连接到Web服务器上 以下载页面 内部运行的计算机编程语言 普通网页内都会插
  • React新出来两个钩子函数是什么?和删掉的will系列有什么区别?

    React新出来两个钩子函数是什么 和删掉的will系列有什么区别 react16废弃的生命周期有3个will componentWillMount componentWillReceiveProps componentWillUpdate
  • SolidWorks二次开发语法技巧及基础

    语法 变量 HRESULT 接口返回值 用于异常调用时判断 本质 typedef LONG HRESULT 32位 S OK S FALSE OLECHAR 特定平台上表示文本数据 win32内 定义为 wchar t 16 或32 位 c
  • 正大国际:做期货交易的方法

    以多为例 空则反之 1 顺势交易 这话估计听的耳朵都起茧子了 但是还是要强调 顺势 顺势 不要抄底 不要摸顶 2 势的判断 做明晃晃的趋势 一眼就看出在上涨的 你就是找个小学生 老太太等完全不懂交易的 让他看走势图 他都知道在上涨 这就是明
  • 区块链知识转载博文1: 共识算法之争(PBFT,Raft,PoW,PoS,DPoS,Ripple)

    注 这是本人读到的关于共识算法最全和最好的分享博文 系统的介绍了拜占庭容错技术以及共识算法的原理和常用共识算法 原文链接请见后 目录 一 拜占庭容错技术 Byzantine Fault Tolerance BFT 二 PBFT Practi
  • 智慧“昆明”在路上 未来充满精彩

    智慧城市是运用物联网 云计算 大数据 移动互联网 空间地理信息集成等新一代信息技术 促进城市规划 建设 管理和服务智慧化的新理念和新模式 近年来 昆明市全面加快智慧城市建设 力争通过三年的努力 打造区域信息辐射中心的核心区 生态 融合发展的
  • Spring Boot系列四 Spring @Value 属性注入使用总结一

    Value注入 不通过配置文件的注入属性的情况 通过 Value将外部的值动态注入到Bean中 使用的情况有 注入普通字符串 注入操作系统属性 注入表达式结果 注入其他Bean属性 注入beanInject对象的属性another 注入文件
  • C语言if选择练习题

    C语言实现输出对应成绩的等级 include
  • hdu 5756:Boss Bo

    题目链接如下 Problem 5756 先用dfs确定每个节点的序号编号 并且可以获得每个节点可以包括的子树节点区间范围 再用线段树建立一棵树 在第一次建立的时候我们记录每个节点的深度 然后再进行一次dfs 这次dfs用来更新以不同节点为根
  • 【实验九】【使用触发器实现数据完整性】

    文章目录 触发器 一 实现域完整性 二 实现参照完整性 三 比较约束与触发器的执行顺序 Reference 触发器 触发器 trigger 是用户定义在关系表上的一类由事件驱动的特殊过程 触发器又叫做事件 条件 动作 event condi
  • ThinkPHP6.0 多应用模式 部署 Layuiadmin 单页版

    QQ 23426945 PHP技术群 159789818 个人技术博客 https www itqaq com TP6 0中的路由省略应用名只能用入口文件绑定应用 和 域名绑定应用 经过测试 最后得出域名绑定应用是最合适的部署方式 如果有更
  • React源码分析3-render阶段(穿插scheduler和reconciler)

    本章将讲解 react 的核心阶段之一 render阶段 我们将探究以下部分内容的源码 更新任务的触发 更新任务的创建 reconciler 过程同步和异步遍历及执行任务 scheduler 是如何实现帧空闲时间调度任务以及中断任务的 触发