6.1 状态管理模式与库


6.1 状态管理模式与库

本节摘要:当"全局状态"需要跨多页共享,本地状态与 props 会捉襟见肘,于是有了集中式的状态库——React 圈常用 Redux,Vue 圈常用 Pinia。本节把"何时该上状态库"与两套库的心智对齐讲透,避免用全局状态库去管本该待在组件里的数据。

本节阅读目标
阅读完本节,你应当能够:

  1. 说出何时本地状态够用、何时需要全局状态库。
  2. 复述 Redux 与 Pinia 各自的心智结构与关键概念。
  3. 在 React 与 Vue 里各搭出一个只读可写的基本 store。

一、什么时候需要全局状态库

一颗"要不要升级到状态库"的信号灯:只有当数据必须被两个以上彼此不相邻的组件反复读到、且它们不共享一个最近的父级时,才值得把数据放进全局 store。反过来说,若数据只在一个子树里用,本地状态或状态提升反而更简单、更好排查。别让全局状态库替你做"什么都放"的懒抽屉。

二、Redux 的心智

Redux 用单向、可预测的写法管理全局状态,核心概念:

  • store:唯一的全局状态容器。
  • action:描述"发生了什么"的普通对象。
  • reducer:根据 action 与旧状态算出新状态的纯函数。
  • dispatch:向 store 提交 action 的唯一入口。

一个极简例子:

function counter(state = 0, action) { switch (action.type) { case 'inc': return state + 1; default: return state; } } // store.dispatch({ type: 'inc' })

它的"单向可预测"来自:只能用 dispatch 触变化、只有 reducer 改状态、reducer 是纯函数。数据流清晰可查,适合复杂、需强溯源的状态。

三、Pinia 的心智

Pinia 是 Vue 生态的现代状态库,概念更靠近"把状态组织成 store 对象":

import { defineStore } from 'pinia'; export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), actions: { increment() { this.count += 1; } }, });

state 管数据、actions 管变化、getters 管派生值。相比 Vuex 脱掉了繁重的样板,心智轻、类型友好。对应到 React 侧,它的位置相当于"更省心的 Redux"。

四、Redux 与 Pinia 对齐表

概念 Redux(React) Pinia(Vue)
全局容器 store store
描述"发生什么" action action
算出新状态 reducer actions 里的方法
触发变化 dispatch(action) 调用 action 方法
派生值 selector getters

两者都把"全局状态 + 明确的改动路径"放一起,只是样板轻重不同——别再被两套名词吓得以为它们是两种世界观,本质一致。

五、图:全局状态库的数据流向

图:Redux / Pinia 的集中式数据流

图:Redux / Pinia 的集中式数据流

六、比稿结论

把"购物车多页共享"放上桌:本地状态会因为页面切换而丢、props 会因为链深而烦,集中式 store 让任何页面都能读写同一份购物车,改动路径还清晰。而"单列表页里一个筛选框"这种数据根本不进 store——本地状态足够。判断依据就一句:共享范围决定存放层级

六·一、加一个"只在局部"的反例做对照

比稿最重要的是不只看"该上库"的案例,还要看清"不该上库"的反例。假设一个商品列表页里有个"当前选中行的展开开关",它只被列表页自己用,页面跳走也不需要保留——这是最典型的本地状态场景。若你图省事把它塞进全局 store,等于让所有页面都背着这份谁都无关的数据:每次进入都要读库、手滑改错了会影响其他组件表现的默认值,调试时还要多记一层"哪个页面往 store 里塞了它"。

所以别把"全局状态库"当成"所有状态的终点站",它只是"跨范围共享状态"的专用仓库。能看到把不该全局的数据管住,才算是真正理解了 6.1 这节——上不上状态库不只是写几行 import,而是先把"谁该拥有这份数据"想明白。

六·二、在组件里"读一个 store"有多轻

整段讲了不少概念,落到组件里其实就几行。Pinia 里组件通过"拿 store 实例"来读写,模板直接用其 state:

// 任一 Vue 组件里 import { useCounterStore } from '@/stores/counter'; const store = useCounterStore(); // 模板:{{ store.count }},按钮里:store.increment()

React 侧配合 Redux Toolkit 的写法,用一个钩子读取、一个钩子提交,同样是"取状态 + 发动作"两手:

import { useAppSelector, useAppDispatch } from '@/store'; function Counter() { const count = useAppSelector((s) => s.counter.count); const dispatch = useAppDispatch(); return <button onClick={() => dispatch({ type: 'counter/inc' })}>次数 {count}</button>; }

注意两端都遵循同一条纪律:改状态不走"直接改 store 里的引用",而是走库规定的动作入口。这样无论 React 还是 Vue,任何页面看到的都是同一份、由同一批动作驱动的状态,谁在什么时候动了它都能沿动作链回溯。这就是为什么"状态库"值得在上面框架之上多一层的理由——它把"全局共享"的混乱,收敛成一条可查、可控的改动流水线。

七、常见坑

  • 把什么都放 store:全局状态膨胀、难溯源,先想本地是否够。
  • 在 reducer 里做副作用:reducer 应纯,请求之类放异步 action。
  • store 放非响应式数据导致不更新:确认用的是状态的响应式通道。

本节要点回顾

  • 升级时机:多页不相邻组件共享才需全局库,本地优先。
  • 两库对齐:Redux 与 Pinia 都是"全局状态 + 明确改动路径"。
  • 单向数据流:只有 action 触发、reducer 改状态,可预测可查。
  • 一句判断:共享范围决定存放层级。

全局状态把跨页数据安了家。下一件工程题是页面之间怎么切换——路由导航,让"地址"驱动视图变化。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U