1、react中的fiber架构
1.Fiber架构背景
react的fiber架构是react 16引入,重新设计了React的协调和渲染机制,目的提高渲染性能
fiber架构:允许将渲染任务分成多个小块并分阶段完成,避免一次性阻塞主线程
16之前使用的是同步渲染,架构是堆栈协调器
在这种机制下组件的渲染是同步的,所有任务一旦开始,就必须执行到完成。
小组件树,这样做法是高效,大型组件树,渲染不发被中断,可能导致卡顿甚至阻塞用户交互。
2.Fiber架构核心理念
Fiber主要创新点是将渲染过程变为可中断,可恢复的过程,通过将渲染任务切分成多个小任务,每个任务成为一个Fieber,Fiber可以被优先级调度器打断,然后在空闲时间继续执行,
- 可中断更新:Fiber允许将渲染任务分成多个小片段,可以在中途暂停并让出控制权给主线程,以便处理更高优先级的任务,如用户输入或动画。
- 优先级调度:不同的更新任务会被赋予不同的优先级,React会优先处理那些高优先级任务如(用户交互),从而提高用户体验。
- 增量渲染:Fiber允许组件的渲染过程逐步进行。
3.Fiber结构
每个Fiber节点表示组件树中的一个单元,包含一下关键信息:
- type:当前节点类型,如函数组件、类组件或者原生DOM元素
- key:用于节点的唯一标识符
- child、sibling、return:构成Fiber树的父子或兄弟节点,形成链表结构,便于便利
- props:组件的传递参数
- stateNode:与Fiber节点的相关联的实例(如DOM节点和组件实例)
- alternate:指向同一节点的上一次渲染的FIber节点,用于比较新旧状态的差异
4.Fiber的工作过程
Fiber架构将渲染过程分为两个阶段:
1.协调阶段:-计算并构造Fiber树
协调阶段的任务是对比当前Fiber树和树的更新,找到需要更新的部分。这个过程可以被打断。异步
- 遍历更新:react会遍历当前组件树并创建Fiber树,通过Diff算法找到更新的节点
- 任务切片:如有更高优先级的任务(如用户交互),React可以暂停当前任务处理高优先级的任务
2.提交阶段:-真是DOM更新
提交阶段是同步指向,不能被打断,主要将协调阶段计算出的更新应用到实际的DOM上,三个步骤
- Before mutation:在DOM发生变化之前调用生命周期钩子(如getSnapshotBeforeUpdate)
- Mutation:执行DOM更新、插入、删除等操作
- Layout:更新后的回调(如componentDidMount、componentDidUpdate)会在此时处罚
5.Fiber的优先级调度
React在Fiber架构中引入优先级调用,他会根据任务优先级分配。常见的优先级包括
- 同步优先级:需要立即处理的关键更新。如输入框中用户输入,最高优先!!!
- 离散事件优先级:如点击按钮等用户操作。属于较高优先级,较高!!
- 低优先级:非关键性渲染任务(动画更新),被分配到低优先级,低!
这种调度机制保证了在大型应用中,React不会因为长时间渲染任务导致界面卡顿,可以优先处理用户交互等高优先级任务。提升整体流畅性。
6.Fiber架构的优势
- 流畅的用户体验:通过将渲染任务拆分为多个小任务,并根据优先级调度,用户在更新渲染期间扔能够进行交互
- 可中断和恢复的渲染:渲染任务可以被打断并在稍后恢复处理,避免长时间任务阻塞主线程
- 任务优先级:能够合理分配高优先级和低优先级的任务,确保更流畅的动画和响应性
- 细粒度控制:Fiber提供了对更新过程的更细粒度控制,使得React在应对负载更新时性能更加出色
7.总结
React Fiber架构通过引入可中断的更新机制和优先级调度,极大的提升了复杂应用的渲染性能。Fiber架构的主要目标时使React更够在大型应用中更高效地处理渲染任务,保证UI交互的流畅性,并减少不必要的重新渲染。
-
react的diff算法
首先 diff算法是同级别比较不跨级别 时间复杂度O(n)
tree层 只有创建和删除操作
component层 会判断是否是同一个类的组件
element层 对应key的比较
2、组件之间的通讯
react是单项数据流
1、父子组件props,子组件调用回调函数可以改变父组件传的值 2、兄弟组件之间 可以借用父组件进行传值 3、跨组件之间的通讯 借用createContext方法,解构出Provide,Consumer组件provide提供数据 Consumer 接收传递的值函数组件的用法:
<Context.Provide value={需要传递的数据}>
//接收数据先从页面解构出Context
import {Context} from './index'
const 公共数据 = useContext(Context)
4、使用es6新特性完成传值 class
export default class ClearTimer {
private static instance = new ClearTimer();
public static get Instance() {
return this.instance;
}
private themenum: string = "";
public setThemenum(xx: string) {
this.themenum = xx;
}
public getThemenum() {
return this.themenum;
}
}
重点 !!!使用useContext遇到的问题
1.跨组件更新问题
问题:当Context之值变化时,所有使用useContext的组件都会重新渲染,不管组件是否依赖值是否变化?
解决方法:
- 使用React.memo:使用
React.memo包裹那些依赖useContext但不需要频繁更新的组件,防止不必要的渲染 - 将上下文值拆分:可以把上下文拆分多个小的context,减少全局状态的更新。如:如果一个组件只需要使用上下文中的某个字段,可以单独为这个字段创建一个context。
2.context值更改引发的无线渲染
如果通过useConetxt使用了一个会动态变化的函数或对象,每次渲染都创建新的对象,会导致无限重新渲染
问题:由于上下文的值是引用类型,当传递给Provide的值是一个新的对象,即便对象的内容相同,React也会认为是一个新值,导致重新渲染。
解决方法:
- 使用usememo或者useCallback:在传递给Provider的值是函数或者对象时,确保使用useMemo或者useCallback来缓存,避免每次渲染生成新的引用
const value = useMemo(() => ({someState}),[someState]);
//或者
const value = useCallback(() => someFuntion(), [dependcies]);
<SomeContext.Provider value={value}>
{xxx}
<SomeContext.Provider/>
3.Context值丢失或者不更新
问题:当上下文状态更新后,某些组件依然没有获取到最新值,可能因为Provider被条件渲染,或组件更新时没有重新订阅到上下文
解决方法:
- 确保Provider始终渲染:不要将
Provider放在条件语句中,这样会导致组件的订阅上下文失效。 - 检查组件的更新逻辑:使用的
shouldComonentUpate或者React.memo来优化性能时,可能会导致子组件没有更新,因此没有获取到最新的上下文值。
4.嵌套过深带来的复杂性 问题:Context层次过多,会导致代码复杂,且难以在某些大型应用中维护上下文传递顺序。
解决方法:
- 合理拆分Context:尽量避免将所有全局状态放在一个上下文中,而是将不同的逻辑拆分成多个Context,比如将用户信息、主题、语言等状态分开管理
- 组合使用多个Provider:React支持组合多个
context.Provider,可以在根组件中将多个Provider嵌套在一起,并通过Context Selector只获取需要的上下文值。
<ThemeContext.Provider value={theme}>
<AuthContext.Provider value={auth}>
<App/>
<AuthContext.Provider/>
</ThemeContext.Provider>
5.与Redux或其他章台管理工具的冲突
问题:React中useContext和全局状态管理工具可能同时管理同一状态或部分相同的状态,导致状态的流向难以追踪,更新不一致。
解决方法:
- 明确使用场景:
useContext适合管理局部的、轻量级的状态,而全局性、复杂性应该交给专门的管理(redux)处理,避免全局和局部都处理相同的状态。 - 大型项目合理架构:如果状态较为复杂,建议采用单一的状态管理工具。
6.SSR(服务器端渲染)中的同步问题 问题:使用SSR场景下,二如果没有正确的状态同步,客户端和服务器渲染都可能导致结构不一致
解决方法:
- 确保初始状态同步:确保在服务端渲染时提供了完整的上下文状态,避免渲染时产生意外
- 使用合适的状态管理工具:在SSR项目中,结合Context和其他工具如(Next.js的getinitialProps)确保状态一致。
3、React的hooks
在hooks出现之前 函数组件是无状态的
在16.8之后 出现了hooks 函数组件变得有状态
useCallback 缓存回调函数,适用于需要传递给子组件的事件处理函数,避免不必要的重新渲染
React.memo 缓存组件
useMemo 相当与vue的计算属性,适用于需要缓存值的场景
useState 存放数据
useRef用于获取dom元素或组件对象
useEffect模拟生命周期
两个参数 第一个参数是回调函数,模拟componentDidUpdate ,componentDidMount ,
第二个参数是数组,为空时模拟,componentDidMount
第二个为空,模拟componentDidMount ,模拟vue中的watch
第一个参数 里return 一个新的函数 模拟compontentWillUnMount
4、 useState是同步还是异步
useState是异步
setState同步异步
在合成事件和生命周期钩子中,setState是异步
在原生和定时器只同步
setState
在合成事件 和 生命周期钩子(除componentDidUpdate) 中,setState是"异步"的;
在 原生事件 和setTimeout 中,setState是同步的,可以马上获取更新后的值;
批量更新:多个顺序的setState不是同步地一个一个执行滴,会一个一个加入队列,然后最后一起执行。在 合成事件 和 生命周期钩子 中,setState更新队列时,存储的是 合并状态(Object.assign)。因此前面设置的 key 值会被后面所覆盖,最终只会执行一次更新。
函数式: setState第一个参数为函数形式时,在这个函数中可以回调拿到最新的state对象,然后函数return出的对象讲被设置成newState。this.setState((state, props) => newState)
5、类组件和函数组件的区别
2.1状态
类组件一直有状态,函数组件在16.8之前没有状态,之后才有状态
2.2写法
类组件是es6写法,需要继承React.Component并且创建render函数返回react元素
函数组件就是纯函数式 编写
2.3 函数组件 :return jsx 类组件:render有函数
2.4 调用方式
类组件需要new 实例化
函数组件 需要重新渲染
2.5生命周期
类组件有生命周期
函数组件没有生命周期
2.6this
类组件有this
函数组件没有this
6、 react中的key
react利用key 识别组件,它相当于一个身份认证
它可以提高我们diff算法的优化效率
key是react用来追踪哪些列表的元素被修改,被添加或者是被删除的辅助标示。在开发过程中我们需要保证某个元素的key在其同级元素中具有唯一性。
新旧虚拟dom比较,
7、# setState是同步还是异步
React18版本为分水岭
react18之后都是异步的
react的合成事件,setState是异步 在setTimeout、setInterval 原生js中它也是异步
react18版本之前
在react的合成事件 它是异步 在setTimeout、setInterval、原生js他是同步
8、父组件获取子组件方法
ref或者forwardRef
9、react state如何获取上一次值
使用箭头函数
10、为什么要使用reduce?
使用reduce 可以提供
11、react合成事件
react的合成事件,屏蔽了浏览器自带的事件, 解决了浏览器的不兼容,
将事件绑定在document上,按照冒泡或者捕获的路径去收集真正的事件处理函数,在此过程中会先处理原生事件,然后当冒泡到document对象后 普通事件 是绑定在真实dom上
12、为什么浏览器无法读取JSX?
浏览器只能处理JavaScript对象,而不能读取常规JavaScript对象中的JSX。所以为了使浏览器能够读取JSX,首先需要像Babel这样的JSX转化器将JSX文件转化为JavaScript对象,然后将其传给浏览器。