一、什么是状态管理
1.为什么会出现
从React诞生之后,前端组件化的方案深入人心,React遵循的是单向数据流的原则,属性通过Props自 上而下的传递。当页面的比较简单,组件之间的层级关系比较浅时,这种自上而下的单向数据流的方式是 不会有问题的。如果页面一复杂,组件的嵌套层级一深,这种单向数据流的传锑方式,将今伸你陷入到“嵌套地狱”。 状态管理本身,解决的就是这种“嵌套”地狱的问题,解决的是跨层级组件之间的数据通信和状态共享。
2.本质
状态管理工具的本质:管理共享内存中的状态。
1.共享内存
2.管理状态
3.页面通信
4.组件通信
5.刷新失效
详细定义:单页应用的各个组件本身是共享内存的,如果将状态保存在内存中,
就可以读写统一内存中的变量,从而达到状态共享的目的。
3.状态管理工具
为什么React有这么多状态管理工具?
Vue: Vuex(Pinia)
Angular: Service和Rxjs
React: Flux、Redux、Mobx、Rxjs、Recoil、Jotai、Zustand
跟不同前端框架的定义有关, Vue和Angular双向数据绑定,计算属性等,数据是响应式的,
控制视图刷新,拥有计算属性等,这些使得Vue和Angular需要状态管理的场景减少,此外其
本身就包含了完整的状态管理工具,比如Vue的Vuex和Pinia,Angular的Service(RXjs)等,从官方定调。
而React不一样, React是一个纯U层的前端框架, Ul = fn(state),React将状态的变动完全交给开发者。
二、React状态管理简介
2-1.分类
React状态管理工具可以分为以下几类:
React自带:Local State(props)和Context
单向数据流:Flux、Redux(Redux-toolkit)
双向数据绑定:Mobx
原子型状态管理:Recoil、Jotai
异步操作密集型:Rxjs
每一种状态管理工具都有其不同的适用性,不同场景下需要合理的选择状态管理工具。
2-2.local state(Props)
(1)最简单的局部local state,组件级别的局部状态管理(生命周期:组件被创建时,初始化和生效。当组件被销毁时,失效。)
(2)将状态向上一级放在父组件中,由父组件自上而下的传递,子组件将通过props接收。
(3)同级组件(或页面)之间的通信,React提供了Context。也可以通过query的方式来实现,?a=1&b=1的形式,监听Rl中的参数,这样就可以进行通信。
(4)context
在这个例子中通过createContex和useContext,可以在App的子组件CounterDisplay中使用context从而实现一定意义上的跨组件通信。context的值一旦变化,所有调用了useContext)的组件均会重新触发渲染更新。由于context APl并不能细粒度地分析某个组件依赖了context里的哪个属性,并且它可以穿透React.memo和dependence的对比,所以针对频繁引起数据变化的场景,在使用时需格外谨慎。
2-3 Redux
(1)介绍
我们前面讲到了props和context,以及他们的优缺点,在讲React状态管理工具的时候,最经典的要属Redux了,我们详细介绍一下Redux.
Redux是从Flux演变而来的, Flux它是Facebook官方给出的应用架构,利用数据的单向流动的形式对公共状态进行管理,不过现在已经被淘汰了,不过其设计思想还是可以参考和借鉴的,在聊Redux之前,我们先聊一下Flux状态管理。
(2)架构
(3)原则
单一数据源,只有一个store、store中的state是只读的、使用纯函数来执行修改。
1.单一数据源:
在redux中,整个应用的全局State(再次注意是全局state),都会保存在一个store中,一个单一数据源state tree也简化了应用的调试和和监控,它也让你在开发中能将应用数据持久化到本地,从而加速开发周期。此外,有一些功能以前很难实现,比如"撤销/重做",在单―数据源的原则下,使用Redux实现将非常容易。
2.Store中的State是只读的:
我们不能直接修改store中的state,store中的state是只读的。唯一能改变store中的state的方式就是通过action
3.使用纯函数来执行修改:
接受纯函数来接受aciton,该纯函数叫reducer,可以改变store中的state.
(4)应用场景
因为Redux的上述特性,使得Redux可以做时间旅行。时间旅行:顾名思义,就是可以随时穿越到以前和未来,让应用程序切换到任意时间的状态。因此,如果复杂的场景,特别是存在页面组件间复杂的通信的场景非常适合用Redux来管理状态。
Redux比较适合用于大型Web项目,尤其是一些交互足够复杂、组件通信频繁的场景,状态可预测和回溯是非常有价值的。还有一种场景,比如需要事故重现,这种定义和上报事故异常和重现的场景,Redux也很有意义。
Redux的缺点也很明显,首先为了实现纯函数的Reducer,Redux必须处理各种各样的副作用,需要引入一系列的副作用中间件,加重的心智负担,此外Action,Dispatch,Reducer的模式需要写过多的样版代码,虽然通过React hooks和Redux toolkit可以减少一定的样板代码,但是复杂度还是摆在哪里。因此中小项目,也不太推荐使用Redux,可能Context或者React hooks中的useReducer就能满足你的需求。
2-4.Mobx
它通过透明的函数响应式编程使得状态管理变得简单和可扩展, Mobx跟Vue的设计比较相似,是一个响应式的状态管理库。Mobx借助于装饰器的实现,使得代码更加简洁易懂。由于使用了可观察对象,所以Mobx可以做到直接修改状态,而不必像Redux一样编写繁琐的actions和reducers。
运行原理图:
2-5.Recoil
(1)介绍
Recoil是React官方内置的状态管理工具,一定程度上解决了Local State和Context的局限性,且能够兼容React的新特性比如Concurrent模式等。
解决的问题:
1.组件间的状态共享只能通过将state提升至它们的公共祖先来实现,但这样做可能导致重新渲染一颗巨大组件树。2.Context 只能存储单一值,无法存储多个各自拥有消费者的值的集合。
(2)特点
Recoil主要特点,就是较为官方,提供了与Concurrent模式及其他React新特性兼容的可能性,主打的是性能。此外因为其原子性的特点,比较容易做到细粒度的状态控制。也能跟Redux实现状态回溯,想比较Redux而言,还有一个特点就是理解起来没有很复杂,不需要写很多样板代码等。
Recoil还有一个特点就是可以实现状态快照。比如填充首屏数据和数据状态回滚等。
2-6.Zustand
Zustand是主打轻量级的状态管理工具,没有Redux那样臃肿的设计,也没有兼容React类组件的历史包袱, Zustand状态管理工具体积很小,因此很适合移动端的网页。
Zustand库的核心API和Redux极为相似,区别主要在状态的更新,Redux通过dispatch和reducer函数来进
行状态更新, 而Zustand则是可以通过setState来直接修改状态。
对比:
zustand通过Object.assign 函数合并更新状态,同时提供replace标志位直接将旧状态完全替换。而redux的状态更新则要复杂一些,主要是官方推荐的编程模式将状态更新拆分为多个步骤,dispatch()函数触发一个Action,而具体处理Action 以及状态合并的操作均由Reducer 函数完成,该函数是一个纯函数。这么设计的原因是纯函数对于状态变化来说是可预测的,而且利于测试,更是实现时间旅行类似功能的基础。
三、实现一个简易的状态管理工具
实现: