本节摘要:当"全局状态"需要跨多页共享,本地状态与 props 会捉襟见肘,于是有了集中式的状态库——React 圈常用 Redux,Vue 圈常用 Pinia。本节把"何时该上状态库"与两套库的心智对齐讲透,避免用全局状态库去管本该待在组件里的数据。
本节阅读目标
阅读完本节,你应当能够:
一颗"要不要升级到状态库"的信号灯:只有当数据必须被两个以上彼此不相邻的组件反复读到、且它们不共享一个最近的父级时,才值得把数据放进全局 store。反过来说,若数据只在一个子树里用,本地状态或状态提升反而更简单、更好排查。别让全局状态库替你做"什么都放"的懒抽屉。
Redux 用单向、可预测的写法管理全局状态,核心概念:
一个极简例子:
function counter(state = 0, action) { switch (action.type) { case 'inc': return state + 1; default: return state; } } // store.dispatch({ type: 'inc' })
它的"单向可预测"来自:只能用 dispatch 触变化、只有 reducer 改状态、reducer 是纯函数。数据流清晰可查,适合复杂、需强溯源的状态。
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(React) | Pinia(Vue) |
|---|---|---|
| 全局容器 | store | store |
| 描述"发生什么" | action | action |
| 算出新状态 | reducer | actions 里的方法 |
| 触发变化 | dispatch(action) | 调用 action 方法 |
| 派生值 | selector | getters |
两者都把"全局状态 + 明确的改动路径"放一起,只是样板轻重不同——别再被两套名词吓得以为它们是两种世界观,本质一致。

把"购物车多页共享"放上桌:本地状态会因为页面切换而丢、props 会因为链深而烦,集中式 store 让任何页面都能读写同一份购物车,改动路径还清晰。而"单列表页里一个筛选框"这种数据根本不进 store——本地状态足够。判断依据就一句:共享范围决定存放层级。
比稿最重要的是不只看"该上库"的案例,还要看清"不该上库"的反例。假设一个商品列表页里有个"当前选中行的展开开关",它只被列表页自己用,页面跳走也不需要保留——这是最典型的本地状态场景。若你图省事把它塞进全局 store,等于让所有页面都背着这份谁都无关的数据:每次进入都要读库、手滑改错了会影响其他组件表现的默认值,调试时还要多记一层"哪个页面往 store 里塞了它"。
所以别把"全局状态库"当成"所有状态的终点站",它只是"跨范围共享状态"的专用仓库。能看到把不该全局的数据管住,才算是真正理解了 6.1 这节——上不上状态库不只是写几行 import,而是先把"谁该拥有这份数据"想明白。
整段讲了不少概念,落到组件里其实就几行。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,任何页面看到的都是同一份、由同一批动作驱动的状态,谁在什么时候动了它都能沿动作链回溯。这就是为什么"状态库"值得在上面框架之上多一层的理由——它把"全局共享"的混乱,收敛成一条可查、可控的改动流水线。
全局状态把跨页数据安了家。下一件工程题是页面之间怎么切换——路由导航,让"地址"驱动视图变化。