【React】常见疑问的整理

1. React渲染一个列表,需要为每一项指定key?

        列表的key主要是为了react渲染阶段更好的协调(reconciliation),diff时更好的识别react元素,减少dom的创建和操作,提升效率。

设定key值方式:

  • 来自数据库的数据,最好是主键(Primary Key)
  • 如果数据是本地产生,可以生成唯一的ID作为key

key需满足条件:

  • key值在兄弟节点之间必须唯一
  • key值不能改变
2. 为什么一定要在函数组件(FC)顶层调用Hook函数?

        以useState举例来说,state和修改state的操作队列一起存在Fiber节点的hooks数组中,形式类似。

fiber.hooks = [{state: '', // 第0个hook的当前状态值queue: [], // 第0个hook待执行的操作队列},{state: '', // 第1个hook的当前状态值queue: [], // 第1个hook待执行的操作队列},{memorizedState: [...,...]}...
];

渲染时,函数组件作为函数执行时,函数内每执行一个hook函数,就按顺序从fiber.hooks取出hook的对应状态和操作队列。如果hook函数不是在函数组件顶层,可能会打乱获取hook对应状态和操作队列的顺序。

3.React运行过程是怎样的?
3.1 render阶段

一、基本过程

  • render
  • performSyncWorkOnRoot 同步(performConcurrentWorkOnRoot 异步)
  • wookLoopSync(workLoopCurrent)
  • performUnitOfWork:创建下一个Fiber节点,并赋值给workInProcess,将workInProcess与已创建的FIber节点连接起来,构成Fiber树
  • beginWork(递):根据传入的Fiber节点创建子Fiber节点,并将这两个Fiber节点连接起来
  • completeWork(归):当某个Fiber节点执行完completeWork,如果其存在兄弟Fiber节点(fiber.sibling !== null),会进入其兄弟Fiber的‘递’阶段;如果不存在兄弟Fiber,会进入父级Fiber的‘归’阶段。
  • ‘递’和‘归’阶段交错执行直到‘归’到rootFiber,至此渲染工作结束。
  • 深度优先:child -> sibling -> return(parent),一直child到叶节点,再到sibling兄弟节点,处理完了再到return父节点

二、beginWork(current | null, workInProgress, renderLanes)

  • update:current.memorizedProps(oldProps)和workInProgress.pendingProps(newProps)
  • mount:根据Fiber.tag不同,进入不同类型的Fiber的创建逻辑

最终,都会进入reconcileChildren方法

  • update:reconcileChildren会将当前组件与该组件上次更新对应的Fiber节点比较(diff算法),将比较的结果生成新Fiber节点
  • mount:reconcileChildren会创建新的子Fiber节点

最后会执行moutChildFibers或reconcileChildFibers,workInProgress.child = moutChildFibers(...)或reconcileChildFibers(...),其中reconcileChildFibers会为生成的Fiber节点带上effectTag属性,moutChildFibers则不会。

fiber.stateNode会在completeWork中创建,mount时只有rootFiber会赋值Placement effectTag,在commit阶段直接一次插入操作。

三、completeWork(current | null, workInProgress, renderLanes)

类似beginWork,completeWork也是针对不同fiber.tag调用不同的处理逻辑

  • update:Fiber节点已存储对应DMO节点,不需要再生成DOM节点
  1. onClick、onChange等回调函数的注册
  2. 处理style prop
  3. 处理DANGEROUSLY_SET_INNER_HTML prop
  4. 处理children prop

fiber.tag为HostComponent时,最主要的逻辑是调用updateHostComponent,在updateHostComponent内部,被处理完的props会赋值给workInProgress.updateQueue,并最终会在commit阶段渲染到页面上。

  • mount
  1. 为Fiber节点生成对应DOM节点
  2. 将子孙DOM节点插入刚生成的DOM节点中
  3. 与update逻辑中的updateHostComponent类似的处理props的过程

mount时,只会在rootFiber存在Placement effectTag,那么commit阶段是如何通过一次插入DOM操作的?原因是completeWorkd中appendAllChildren方法,由completeWork属于‘归’阶段调用函数,每次调用appendAllChildren时都会将已生成的子孙DOM节点插入当前生成的DOM节点下,‘归’到rootFiber时,已经构建好了离屏DOM树。

在completeWork的上层函数completeUnitOfWork中,每执行完completeWork且存在effectTag的Fiber节点会被保存在一条被称为effectList的单向链表中。

在‘归’阶段,所有有effectTag的Fiber节点都会被追加在effectList中,最终形成一条以rootFiber.firstEffect为起点的单向链表。rootFiber.firstEffect.nextEffect.nextEffect....

结尾:render全部工作完成,在performSyncWorkOnRoot函数中,fiberRootNode被传递给commitRoot方法,开启commit阶段工作流程。

3.2 commit阶段

commitRoot(root) commit阶段的起点。

一、commit阶段主要工作

  • before mutation阶段(执行DOM操作前)
  • mutation阶段(执行DOM操作)
  • layout阶段(执行DOM操作后)

before mutation阶段之前 和 layout阶段之后,还有一些额外工作:useEffect的触发、优先级相关的重置、ref的绑定/解绑

before mutation阶段之前:主要做一些变量赋值,状态重置的工作

layout阶段之后:

  • useEffect相关的处理。
  • 性能追踪相关。
  • 在commit阶段会触发一些生命周期钩子(如 componentDidXXX)和hook(如useLayoutEffect、useEffect)

各阶段的工作

  • before mutation阶段

        整个过程就是遍历effectList并调用commitBeforeMutationEffects函数处理,

  1. 处理DOM节点渲染/删除后的 autoFocus、blur 逻辑
  2.  调用getSnapshotBeforeUpdate生命周期钩子
  3. 调度useEffect
  • mutation阶段

        mutation阶段也是遍历effectList,执行函数。这里执行的是commitMutationEffects

  1. 根据ContentReset effectTag重置文字节点
  2. 更新ref
  3. 根据effectTag分别处理,其中effectTag包括(Placement | Update | Deletion | Hydrating)
  • layout阶段

        与前两个阶段类似,layout阶段也是遍历effectList,执行函数。 具体执行的函数是commitLayoutEffects

  1. commitLayoutEffectOnFiber(调用生命周期钩子和hook相关操作):componentDidMount、componentDidUpdate、useEffect的销毁和回调(先高度,layout结束后再异步执行)、useLayoutEffect回调(mutation阶段调用useLayoutEffect的销毁函数、layout阶段同步执行回调)
  2. commitAttachRef(赋值 ref)

结束:root.current = finishedWork;

3.3 Diff算法
  • 只对同级元素进行Diff,如果一个DOM节点在前后两次更新跨越层级,React则不利用它。
  • 两个不同类型的元素产生不同的树,如果元素由p变成p,React会销毁div及其子孙节点,并新建p及其子孙节点
  • 开发者可通过key prop来暗示哪些子元素在不同的渲染下能保持稳定。

reconcileChildFibers

  • 当newChild类型为object、number、string,代表同级只有一个节点 - reconcileSingleElement
  • 当newChild类型为Array,同级有多个节点 - reconcileChildFibers

参见文-章:

Build your own React (pomb.us)

React技术揭秘 (iamkasong.com)

注:以上,如有不合理之处,还请帮忙指出,大家一起交流学习~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/611056.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openssl3.2 - 官方dmeo学习 - server-arg.c

文章目录 openssl3.2 - 官方dmeo学习 - server-arg.c概述笔记备注END openssl3.2 - 官方dmeo学习 - server-arg.c 概述 TLS服务器, 等客户端来连接; 如果客户端断开了, 通过释放bio来释放客户端socket, 然后继续通过bio读来aceept. 笔记 对于开源工程, 不可能有作者那么熟悉…

Python操作excel-读取、表格填充颜色区分

1.场景分析 遇到一个需要读取本地excel数据,处理后打入到数据库的场景,使用java比较重,python很好的解决了这类问题 2.重难点 本场景遇到的重难点在于: 需要根据表格内的背景颜色对数据进行筛选 读取非默认Sheet 总是出现Value…

Android Framework 常见解决方案(26)user版本可root方案

1 原理说明 User版本默认是没有root权限和remount功能的,一般该方法用于调试性能相关问题。如果使用debug版本对照,差异过大,因此就有了这样的需求。 修改的核心原理就是调整adbd及相关属性中的一些判定,即user和debug版本的区别…

C 语言关于sizeof() 和 strlen()区别?

sizeof() 和 strlen() 在 C 语言中两个非常常用,它们都与计算内存大小有关,但是它们的作用是不同的。 sizeof() 和 strlen() 的主要区别在于: sizeof() 是一个运算符,而 strlen() 是一个函数。sizeof() 计算的是变量或类型所占用…

day-05 删除子串后的字符串最小长度

思路 通过不断地检查是否含有"AB"或"CD"&#xff0c;如果有则将其从字符串中删除&#xff0c;直到"AB"或"CD"都不存在时&#xff0c;返回字符串的长度 解题方法 //检测是否有"AB" for(int i0;i<len-1;i){ if(s.charAt(i…

Python画国旗

前言 今天&#xff0c;我们来用turtle库来绘制国旗 一、美国国旗 国旗的形状是长方形;国旗的长宽之比为19:10&#xff0c;美国国旗由红、白、蓝三色组成;画面格局由两部分组成&#xff0c;旗的左上方蓝底上排列着50颗白色的星&#xff0c;6颗一排与5颗一排相间排列&#xff…

鸿蒙开发已解决-ArkTS开发webview,html页面中的input和按钮等操作均无响应

文章目录 项目场景:问题描述原因分析:解决方案(根据此方法即可解决此Bug):本文相关知识本Bug常规排除步骤ArkTS项目场景: 在鸿蒙开发过程遇到的问题: 问题 ArkTS API9 使用webview加载的html,页面中的按钮和input等操作均无响应 是有相关API设置webview是否可以touch或…

STM32(Cortex-M) 双堆栈MSP/PSP科普

https://blog.csdn.net/Guet_Kite/article/details/76020069 https://blog.csdn.net/zhuimeng_ruili/article/details/118163303 MSP/PSP是什么?作用&#xff1f; MSP主堆栈指针&#xff0c;PSP进程堆栈指针。 MSP&#xff1a;复位后缺省使用的堆栈指针&#xff0c;用于操作…

创建一个郭德纲相声GPTs

前言 在这篇文章中&#xff0c;我将分享如何利用ChatGPT 4.0辅助论文写作的技巧&#xff0c;并根据网上的资料和最新的研究补充更多好用的咒语技巧。 GPT4的官方售价是每月20美元&#xff0c;很多人并不是天天用GPT&#xff0c;只是偶尔用一下。 如果调用官方的GPT4接口&…

K8S存储卷和数据卷

容器内的目录和宿主机的目录进行挂载 容器在系统上的生命周期是短暂的&#xff0c;delete&#xff0c;k8s用控制器创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会恢复到初始状态&#xff0c;一旦回到初始状态&#xff0c;所有的后天编辑的文件都会消失 容器…

年轻人2023消费图鉴,媒介盒子为你揭秘

回顾近一年的消费&#xff0c;发现大家差不多都是“扣扣嗖嗖的花了很多钱”&#xff0c;如果总结2023年的大众消费关键词&#xff0c;那一定是更加“理性”&#xff0c;据艾瑞咨询《2023年中国消费者洞察白皮书》显示&#xff0c;“精细化”成为2023年的消费关键词&#xff0c;…

十二、QProgressBar的简单使用与样式优化(Qt5 GUI系列)

目录 一、设计需求 二、实现代码 三、代码解析 四、总结 五、扩展(自定义QProgressBar样式) 一、设计需求 在很多应用程序中&#xff0c;在执行费时操作时都会展示一个进度条来展示操作进行的进度。常见的场景&#xff0c;如&#xff1a;拷贝操作、安装操作以及卸载操作。…

Unity组件开发--AB包打包工具

1.项目工程路径下创建文件夹&#xff1a;ABundles 2.AB包打包脚本&#xff1a; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement;public class AssetBundle…

Vue入门三(表单控制|购物车案例|v-model进阶|与后端交互|计算属性|监听属性|Vue生命周期)

文章目录 一、表单控制二、购物车案例三、v-model进阶四、与后端交互跨域问题解决&#xff0c;三种交互方法跨域问题详解1-CORS&#xff1a;后端代码控制&#xff0c;上面案例采用的方式1) 方式一&#xff1a;后端添加请求头2) 方式二&#xff1a;编写中间件3) 方式三&#xff…

代理IP连接不上?网速过慢?自查与解决方法

当您使用代理时&#xff0c;您可能会遇到不同的代理错误代码显示代理IP连不通、访问失败、网速过慢等种种问题。 在本文中中&#xff0c;我们将讨论您在使用代理IP时可能遇到的常见错误、发生这些错误的原因以及解决方法。 一、常见代理服务器错误 当您尝试访问网站时&#…

Mysql系列-1.Mysql基本使用

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

RT-Thread基于AT32单片机的485应用开发(二)

在上篇RT-Thread基于AT32单片机的485应用开发&#xff08;一&#xff09;中实现了RS485收发&#xff0c;但总觉得效率不高&#xff0c;函数封装也不完善。考虑到RS485总线应用都是主从式结构&#xff0c;比如工业领域常用的Modbus协议&#xff0c;都是以帧为单位进行收发&#…

【STK】手把手教你利用STK进行光电红外仿真04-STK/EOIR模块的实战操作1航天器星载相机对LEO卫星成像

STK/EOIR模块的实战操作1 任务目标及要求实战操作全过程1.建立仿真场景及环境2.目标航天器轨道设置3.跟踪航天器轨道设置4.EOIR传感器环境设置5.EOIR属性设置6.合成场景图像生成EOIR模块的这一系列前一部分如下: 【STK】手把手教你利用STK进行光电红外仿真01-STK/EOIR模块概述…

【python】内存管理和数据类型问题

一、内存管理 Python有一个自动内存管理机制&#xff0c;但它并不总是按照期望的方式工作。例如&#xff0c;如果创建了一个大的列表或字典&#xff0c;并且没有删除它&#xff0c;那么这个对象就会一直占用内存&#xff0c;直到Python的垃圾回收器决定清理它。为了避免这种情…