沐游虞笔记
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 基础篇

    • 序章:React课程介绍
    • JSX 基础语法
    • React 基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React-redux 介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • React和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟 DOM
    • React 整体架构
    • React 渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduler调度普通任务
    • Scheduler调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit 工作流程
    • lane模型
    • React 中的事件
    • Hooks原理
    • useState和useReducer
    • effect相关hook
    • useCallback和useMemo
    • useRef
    • Update
      • Update 数据结构
      • UpdateQueue
      • 真题解答
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailout和ContextAPI
    • 性能优化对日常开发启示
  • react
  • 就业篇
luzhichang
2024-10-17
目录

Update

# Update

面试题:说一说 React 中的 updateQueue

在 React 中,有许多触发状态更新的方法,比如:

  • ReactDOM.createRoot
  • this.setState
  • this.forceUpdate
  • useState dispatcher
  • useReducer dispatcher

虽然这些方法执行的场景会有所不同,但是都可以接入同样的更新流程,原因是因为它们使用同一种数据结构来表示更新,这种数据结构就是 Update。

# Update 数据结构

在 React 中,更新实际上是存在优先级的,其心智模型有一些类似于“代码版本管理工具”。

举个例子,假设现在我们在开发一个软件,当前软件处于正常的迭代中,拥有 A、B、C 三个正常需求,此时突然来了一个紧急的线上 Bug,整体流程如下:

image-20230307143812188

为了修复线上 Bug D,你需要先完成需求 A、B、C,之后才能进行 D 的修复,这样的设计实际上是不合理的。

有了代码版本管理工具之后,有紧急线上 Bug 需要修复时,可以先暂存当前分支的修改,在 master 分支修复 Bug D 并紧急上线:

image-20230307144156888

当 Bug 修复完毕后,再正常的来迭代 A、B、C 需求,之后的迭代会基于 D 这个版本:

image-20230307144357498

并发更新的 React 也拥有相似的能力,不同的 update 是有不同的优先级,高优先级的 update 能够中断低优先级的 update,当高优先级的 update 完成更新之后,后续的低优先级更新会在高优先级 update 更新后的 state 的基础上再来进行更新。

接下来我们来看一下 Update 的一个数据结构。

在前面我们说了在 React 中,有不同的触发更新的方法,不同的方法实际上对应了不同的组件:

  • ReactDOM.createRoot 对应 HostRoot
  • this.setState 对应 ClassComponent
  • this.forceUpdate 对应 ClassComponent
  • useState dispatcher 对应 FunctionComponent
  • useReducer dispatcher 对应 FunctionComponent

不同的组件类型,所对应的 Update 的数据结构是不同的。

HostRoot 和 ClassComponent 这一类组件所对应的 Update 数据结构如下:

function createUpdate(eventTime, lane){
  const update = {
    eventTime,
    lane,
		// 区分触发更新的场景
    tag: UpdateState,
    payload: null,
    // UI 渲染后触发的回调函数
    callback: null,
    next: null,
  };
  return update;
}

在上面的 Update 数据结构中,tag 字段是用于区分触发更新的场景的,选项包括:

  • ReplaceState:代表在 ClassComponent 生命周期函数中直接改变 this.state
  • UpdateState:默认情况,通过 ReactDOM.createRoot 或者 this.setState 触发更新
  • CaptureUpdate:代表发生错误的情况下在 ClassComponent 或 HostRoot 中触发更新(比如通过 getDerivedStateFormError 方法)
  • ForceUpdate:代表通过 this.forceUpdate 触发更新

接下来来看一下 FunctionComponent 所对应的 Update 数据结构:

const update = {
  lane,
  action,
  // 优化策略相关字段
  hasEagerState: false,
  eagerState: null,
  next: null
}

在上面的数据结构中,有 hasEagerState 和 eagerState 这两个字段,它们和后面要介绍的 React 内部的性能优化策略(eagerState 策略)相关。

在 Update 数据结构中,有三个问题是需要考虑:

  • 更新所承载的更新内容是什么

对于HostRoot以及类组件来讲,承载更新内容的字段为 payload 字段

// HostRoot
ReactDOM.createRoot(rootEle).render(<App/>);                                   
// 对应 update
{
	payload : {
    // HostRoot 对应的 jsx,也就是 <App/> 对应的 jsx
  	element                                  
  },
  // 省略其他字段
}

// ClassComponent 情况1
this.setState({num : 1})
// 对应 update
{
  payload : {
    num: 1
  },
  // 省略其他字段
}

// ClassComponent 情况2
this.setState({num : num => num + 1})
// 对应 update
{
  payload : {
    num: num => num + 1
  },
  // 省略其他字段
}

对于函数组件来讲,承载更新内容的字段为 action 字段

// FC 使用 useState 情况1
updateNum(1);
// 对应 update
{
  action : 1,
  // 省略其他字段
}

// FC 使用 useState 情况2
updateNum(num => num + 1);
// 对应 update
{
  action : num => num + 1,
  // 省略其他字段
}
  • 更新的紧急程度:紧急程度是由 lane 字段来表示的
  • 更新之间的顺序:通过 next 字段来指向下一个 update,从而形成一个链表。

# UpdateQueue

上面所介绍的 update 是计算 state 的最小单位,updateQueue 是由 update 组成的一个链表,updateQueue 的数据结构如下:

const updateQueue = {
  baseState: null,
  firstBaseUpdate: null,
  lastBaseUpdate: null,
  shared: {
    pending: null
  }
}
  • baseState:参与计算的初始 state,update 基于该 state 计算新的 state,可以类比为心智模型中的 master 分支。
  • firstBaseUpdate 与 lastBaseUpdate:表示更新前该 FiberNode 中已保存的 update,以链表的形式串联起来。链表头部为 firstBaseUpdate,链表尾部为 lastBaseUpdate。
  • shared.pending:触发更新后,产生的 update 会保存在 shared.pending 中形成单向环状链表。计算 state 时,该环状链表会被拆分并拼接在 lastBaseUpdate 后面。

举例说明,例如当前有一个 FiberNode 刚经历完 commit 阶段的渲染,该 FiberNode 上面有两个“由于优先级低,导致在上一轮 render 阶段并没有被处理的 update”,假设这两个 update 分别名为 u0 和 u1

fiber.updateQueue.firstBaseUpdate = u0;
fiber.updateQueue.lastBaseUpdate = u1;
u0.next = u1;

那么假设在当前的 FiberNode 上面我们又触发了两次更新,分别产生了两个 update(u2 和 u3),新产生的 update 就会形成一个环状链表,shared.pending 就会指向这个环状链表,如下图所示:

image-20230307165309276

之后进入新的一轮 render,在该 FiberNode 的 beginWork 中,shared.pending 所指向的环状链表就会被拆分,拆分之后接入到 baseUpdate 链表后面:

image-20230307165521236

接下来就会遍历 updateQueue.baseUpdate,基于 updateQueue.baseState 来计算每个符合优先级条件的 update(这个过程有点类似于 Array.prototype.reduce),最终计算出最新的 state,该 state 被称之为 memoizedState。

因此我们总结一下,整个 state 的计算流程可以分为两步:

  • 将 shared.pending 所指向的环状链表进行拆分并且和 baseUpdate 进行拼接,形成新的链表
  • 遍历连接后的链表,根据 wipRootRenderLanes 来选定优先级,基于符合优先级条件的 update 来计算 state

# 真题解答

题目:面试题:说一说 React 中的 updateQueue

参考答案:

update 是计算 state 的最小单位,一条 updateQueue 代表由 update 所组成的链表,其中几个重要的属性列举如下:

  • baseState:参与计算的初始 state,update 基于该 state 计算新的 state,可以类比为心智模型中的 master 分支。
  • firstBaseUpdate 与 lastBaseUpdate:表示更新前该 FiberNode 中已保存的 update,以链表的形式串联起来。链表头部为 firstBaseUpdate,链表尾部为 lastBaseUpdate。
  • shared.pending:触发更新后,产生的 update 会保存在 shared.pending 中形成单向环状链表。计算 state 时,该环状链表会被拆分并拼接在 lastBaseUpdate 后面。

整个 state 的计算流程可以分为两步:

  • 将 shared.pending 所指向的环状链表进行拆分并且和 baseUpdate 进行拼接,形成新的链表
  • 遍历连接后的链表,根据 wipRootRenderLanes 来选定优先级,基于符合优先级条件的 update 来计算 state
useRef
性能优化策略之eagerState

← useRef 性能优化策略之eagerState→

Theme by Vdoing | Copyright © 2021-2024 蜀ICP备2024068710号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式