如果你还在用 Vuex 管理 Vue 3 项目的状态,是时候认真考虑迁移到 Pinia 了。Pinia 是 Vue 官方团队开发的新一代状态管理库,已经成为 Vue 3 的官方推荐方案。它不只是 Vuex 的升级版,而是一次彻底的重新设计。
一、Vuex 的痛点
在聊 Pinia 之前,先说说 Vuex 让人头疼的地方。
最让人抓狂的是 mutations 的存在。Vuex 规定修改 state 必须通过 mutation,而 mutation 必须是同步的,异步操作要放在 action 里,action 再提交 mutation。这套流程在小项目里显得繁琐,在大项目里又容易让人搞混 action 和 mutation 的职责边界。
其次是 TypeScript 支持很差。Vuex 4 虽然支持 Vue 3,但 TypeScript 类型推导非常有限,写起来需要大量手动标注类型,体验很差。
还有模块嵌套的复杂性。大型项目中 Vuex 的模块系统需要处理命名空间、模块注册等问题,代码组织起来比较繁琐。
二、Pinia 的设计哲学
Pinia 的设计思路非常清晰:去掉 mutations,让 actions 直接修改 state。这一个改动就解决了 Vuex 最大的痛点。
Pinia 的 Store 由三部分组成:state(数据)、getters(计算属性)、actions(方法)。没有 mutations,没有命名空间,没有嵌套模块。每个 Store 都是独立的,需要的时候直接引入使用,Store 之间也可以互相调用。
更重要的是,Pinia 完全基于 Vue 3 的响应式系统构建,TypeScript 类型推导开箱即用,不需要任何额外配置。
三、两种 Store 写法
Pinia 支持两种风格的 Store 定义,可以根据团队习惯选择。
Options Store 风格和 Vue 2 的 Options API 类似,结构清晰,适合从 Vuex 迁移的团队:state 定义数据,getters 定义计算属性,actions 定义方法,一目了然。
Setup Store 风格则和 Vue 3 的 Composition API 完全一致,用 ref/reactive 定义响应式数据,用 computed 定义计算属性,普通函数就是 actions,最后 return 出去。这种写法更灵活,也更符合 Vue 3 的编程范式,是目前更推荐的写法。
两种写法功能完全等价,可以在同一个项目中混用,不同 Store 用不同风格都没问题。
四、在组件中使用 Store
使用 Pinia Store 时有一个常见的坑:直接解构 Store 会失去响应式。
比如 const { count } = useCounterStore(),这样解构出来的 count 是一个普通的数字,不是响应式的,修改 Store 中的 count 不会触发组件更新。
正确的做法是使用 Pinia 提供的 storeToRefs 函数:const { count } = storeToRefs(store)。这样解构出来的 count 是一个 ref,保持了响应式。注意 actions 不需要用 storeToRefs,直接从 store 上解构就行。
五、Store 持久化
状态管理一个常见需求是持久化——页面刷新后状态不丢失。Pinia 有一个非常好用的插件 pinia-plugin-persistedstate,安装后只需要在 Store 定义时加一个 persist: true 配置,就能自动把 state 存到 localStorage,页面刷新后自动恢复。
这个插件还支持细粒度配置:可以指定只持久化某些字段(比如 token 需要持久化,但临时的 loading 状态不需要),可以选择存储位置(localStorage 还是 sessionStorage),可以自定义序列化方式。
六、Store 之间的通信
Pinia 的模块化设计让 Store 之间的通信变得非常简单。在一个 Store 的 action 里,直接调用另一个 Store 的 useXxxStore() 就能获取到那个 Store 的实例,然后读取它的 state 或调用它的 actions。
这比 Vuex 的模块间通信要直观得多。Vuex 中跨模块访问需要用命名空间前缀,或者通过 rootState/rootGetters,写起来很繁琐。Pinia 完全没有这个问题。
七、从 Vuex 迁移的建议
如果你有一个现有的 Vuex 项目想迁移到 Pinia,不需要一次性全部迁移。Pinia 和 Vuex 可以在同一个项目中共存,可以逐个模块迁移。
迁移的思路很简单:把 Vuex 的 state 对应到 Pinia 的 state,getters 对应 getters,把 mutations 和 actions 合并成 Pinia 的 actions。去掉 commit 和 dispatch,直接调用 actions 方法。
迁移完成后,你会发现代码量明显减少,TypeScript 类型提示也变得完善,整体开发体验提升非常明显。
八、总结
Pinia 的成功在于它做了正确的减法:去掉了 mutations 这个不必要的概念,去掉了复杂的模块嵌套,去掉了繁琐的 TypeScript 配置。剩下的是一套简单、直观、类型安全的状态管理方案。
对于新项目,直接用 Pinia;对于老项目,可以逐步迁移。Vue 官方已经把 Pinia 作为推荐方案,Vuex 也不再积极维护,迁移是迟早的事。
更多 Vue 实战内容,欢迎持续关注冉冉博客。













暂无评论内容