react18.2.0
在 18 之前,只有在react事件处理函数中,才会自动执行批处理,其它情况会多次更新 在 18,任何情况都会自动执行批处理,多次更新始终合并为一次
hydrate 变为hydrateRoot render改为createRoot().render()
flushSync 退出批量更新,但是内部仍然是批量更新
react17 和 react18的区别就是:从同步不可中断更新变成了异步可中断更新。
useInsertionEffect
在dom生成之后,useLayoutEffect之前,它的工作原理大致合useLayoutEffect相同,只是此时无法访问DOM节点的引用,一般用于提前注入脚本。
fiber架构的理解
15架构采用的是递归 遇到长任务会阻塞 造成用户卡顿
fiber是react现在的链式数组结构 执行异步的调度任务会在宏任务中执行,这样可以保证,不会让用户失去响应
解决的问题
- 增量渲染。将渲染工作拆分成多个时间片段执行,使得每个时间片段都有机会插入其他优先级更高的任务,保证页面响应性的同时尽可能快地完成渲染工作。
- 优先级调度。引入了任务优先级的概念,根据任务的紧急程度和重要性对任务进行优先级排序,确保优先级较高的任务能够尽早得到处理,提高用户交互的流畅度。
- 可中断和恢复。支持任务的中断和恢复,允许在渲染过程中处理更高优先级的任务,以确保更及时地响应用户操作。
- 更好的错误处理。每个Fiber都有自己的错误边界,可以捕获并处理组件树中发生的错误,并在不崩溃整个应用程序的情况下进行优雅降级。
生命周期
static getDerivedStateFromProps(nextProps,prevState)
render()
componentDidMount()
getSnapshotBeforeUpdate(prevProps,prevState)
componentDidUpdate(prevProps,prevState,snapshot)
Static getDerivedStateFromError() 更改状态降级组件
7个生命周期 2个静态方法
React事件和原生事件执行顺序
react原生事件
react合成事件
root上挂载事件
react事件是怎么注册的
React 代码执行时,顶层会自动执行事件的注册,初始化事件插件。
React 首次渲染时,会在根节点上绑定所有原生事件。支持冒泡的事件,
React 会同时绑定捕获阶段和冒泡阶段的事件;不支持冒泡的事件,会将事件绑定在具体 DOM 元素上。
事件触发前会从目标元素的 Fiber 节点向上收集同类型事件队列,构造合成对象,同类型的事件会复用同一个合成事件实例对象。
根据监听的事件阶段,决定顺序还是倒序遍历执行事件处理函数(模拟事件的冒泡捕获机制)。
React合成事件的优势:
抹平不同浏览器直接的差异,提供统一的API使用体验
通过事件委托的方式统一绑定和分发事件,有利于提升性能,减少内存消耗
react渲染流程:
Scheduler(调度器)根据优先级lanes区分不同的任务类型,其中同步任务立即同步执行在主线程执行,最快渲染出来
异步任务走Scheduler宏任务里执行的
通过 requestAnimationFrame + 超时时间 + messageChannel 控制那些任务优先进入 Reconciler
Reconciler(协调器)负责构建fibertree,找出变化的组件,标记组件的变化
react更新时会从fiberroot从上到下开始遍历,找到变化的节点,最终形成一颗fiberTree叫workInProgressTree
然后将fiberRootNode的current指向workInProgress 完成fiber tree的更新
lifecycle 生命周期阶段
调用生命周期方法
render渲染阶段
React会根据组件的状态变化、props的更新或者⽗组件的重新渲染等触发条件,重新执⾏组件的函数体(函数组件)或者render⽅法(类组件)。
当React执⾏函数组件或render⽅法时,它会检测组件中是否包含了Hooks,如果包含了Hooks,那么React会根据Hooks的顺序依次调⽤它们
commit 阶段
将render阶段生成的更新应用到真实的dom上,完成界面的渲染
useTranstion和useDeferredValue异同:
相同点: useDeferredValue本质上和内部实现与useTranstion一样都是标记成了过度更新任务。
不同点:useTranstion是把startTranstion内部的更新任务变成了过度任务transtion,而useDeferredValue是把原值通过过度任务得到新的值,这个值作为延时状态,一个是处理逻辑,一个是生产一个新的状态。
React Diff 会预设几个规则:
- 只对同级节点,进行比较
- 节点变化,直接删除,然后重建
- 存在key值,对比节点的key值
其中单节点Diff相对简单,包含以下流程:
- 首先会判断老的Fiber树上有没有对应的Fiber节点,若没有则说明是新增操作,直接在老Fiber树上新增节点并更新DOM
- 若老Fiber节点也存在,则判断节点上的
key
值是否相同,若不同则删除老节点并新增新节点 - 若
key
值相同,则判断节点的type
是否相同,若不同则删除老节点并新增节点 - 若
type
值也相同,则认为是一个可复用的节点,直接返回老节点就行
多节点的Diff操作主要用于map返回多个相同节点的情况下,可以分为三种情况:新增节点、删除节点以及节点移动,React采用双重遍历的方式来进行三种情况的判断,流程如下:
- 第一轮遍历会依次将 children[i] 和 currentFiber 以及 children[i++] 和 currentFiber.sibling 进行对比,当发现节点不可复用时提前结束遍历
- 当第一轮遍历无提前结束时,说明所有节点都可以复用,直接返回老节点
- 若children遍历完成,currentFiber未完成,则说明是删除操作,需要对未完成的 currentFiber 兄弟节点标记删除
- 若children遍历未完成,currentFiber完成,则说明是新增操作,需要生成新的workInProgressFiber节点
- 若children和currentFiber都未完成,则说明是节点位置发送了变更,那就对剩余的currentFiber进行遍历,并通过key值找到每一个节点在children中对应的老节点,并将老节点中的位置替换为新节点的