随着 Vue 3 的普及,全局状态管理方案的选择成为许多开发者关注的话题。Vue 3 带来了 Composition API,也让状态管理有了更多可能性。本文将对比三种主流方案:Pinia、Vuex 以及基于 Composition API 的自定义 composable,帮助你根据项目需求做出合适的选择。
为什么需要全局状态管理
在 Vue 应用中,组件之间经常需要共享状态。例如用户登录信息、购物车数据、主题设置等。如果仅靠 props 和事件传递,随着组件层级加深,代码会变得难以维护。全局状态管理提供了一种集中式的解决方案,让状态变更可预测、可追踪。
Vue 3 生态中,官方推荐的状态管理库从 Vuex 逐渐转向了 Pinia。但除了这两个库,我们还可以利用 Composition API 自己封装 composable 来实现轻量级的状态共享。
Pinia:新一代官方推荐
Pinia 是 Vue 官方团队推荐的状态管理库,专为 Vue 3 设计(也支持 Vue 2.7+)。它摒弃了 Vuex 中的 mutations,只保留 state、getters 和 actions,让代码更加简洁。
核心特点
- 极简 API:没有 mutations,actions 可以直接修改 state,同步或异步均可。
- 完整的 TypeScript 支持:类型推断非常出色,无需额外类型定义。
- 模块化设计:每个 store 独立定义,无需嵌套模块。
- Composition API 风格:可以像写 setup 函数一样定义 store。
- DevTools 支持:与 Vue DevTools 深度集成,调试方便。
示例代码
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
在组件中使用:
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<button @click="counter.increment">{{ counter.count }}</button>
</template>
适用场景
Pinia 适合绝大多数 Vue 3 项目,尤其是中大型应用。它的学习曲线平缓,类型支持优秀,是目前最主流的选择。
Vuex:经典但逐渐退场
Vuex 是 Vue 2 时代的官方状态管理库,Vue 3 也支持 Vuex 4。它采用 Flux 架构,核心概念包括 state、getters、mutations、actions 和 modules。
核心特点
- 严格的单向数据流:state 只能通过 mutations 修改,actions 提交 mutations。
- 模块系统:支持嵌套模块,适合大型项目组织代码。
- 成熟的生态:大量现有项目和文档。
- 插件系统:支持持久化、日志等插件。
示例代码
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: { count: 0 },
getters: {
double: (state) => state.count * 2
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => commit('increment'), 1000)
}
}
})
局限性
- 样板代码多:需要定义 mutations 和 actions,即使是很简单的操作。
- TypeScript 支持较弱:需要手动声明类型,体验不如 Pinia。
- 概念复杂:对于新手来说,mutations 和 actions 的区分需要时间理解。
- Vue 3 支持滞后:Vuex 4 虽然兼容 Vue 3,但官方已不再积极推荐。
适用场景
如果维护已有的 Vue 2 项目,或者团队已经熟悉 Vuex,可以继续使用。对于新项目,建议优先考虑 Pinia。
自定义 composable:轻量而灵活
Vue 3 的 Composition API 允许我们创建可复用的组合函数(composable)。对于简单的全局状态,完全可以自己封装一个 composable,而不引入额外的库。
实现方式
利用 ref、reactive 等响应式 API,配合模块作用域,可以实现单例状态。
// composables/useCounter.js
import { ref, computed } from 'vue'
const count = ref(0) // 模块作用域,天然单例
export function useCounter() {
const increment = () => count.value++
const double = computed(() => count.value * 2)
return { count, increment, double }
}
在组件中使用:
<script setup>
import { useCounter } from '@/composables/useCounter'
const { count, increment } = useCounter()
</script>
优点
- 零依赖:不需要安装任何库。
- 完全可控:代码量少,逻辑清晰,适合小型项目或特定场景。
- 灵活:可以自由组合其他 composable。
- 类型友好:TypeScript 推断自然。
缺点
- 缺少 DevTools 支持:无法像 Pinia/Vuex 那样在开发者工具中查看状态变更。
- 没有插件生态:持久化、时间旅行调试等需要自己实现。
- 团队规范依赖:需要约定命名和结构,否则容易混乱。
- SSR 注意事项:在服务端渲染时,模块作用域的单例可能导致跨请求状态污染,需要谨慎处理。
适用场景
- 小型项目或原型开发。
- 只需要共享少量状态。
- 对包体积敏感的场景。
- 希望完全掌控状态逻辑。
三者对比总结
| 特性 | Pinia | Vuex | 自定义 composable |
|---|---|---|---|
| 学习曲线 | 低 | 中 | 低 |
| TypeScript 支持 | 优秀 | 一般 | 优秀 |
| 样板代码 | 少 | 多 | 极少 |
| DevTools | 支持 | 支持 | 不支持 |
| 插件生态 | 丰富 | 丰富 | 无 |
| 适用规模 | 中大型 | 中大型 | 小型 |
| 官方推荐 | 是 | 否(维护模式) | 视情况 |
如何选择
- 新项目:优先选择 Pinia。它是 Vue 官方推荐,API 简洁,类型支持好,生态完善。
- 维护旧项目:如果已使用 Vuex,可以继续保留,但新模块可以考虑逐步迁移到 Pinia。
- 小型项目或简单共享:自定义 composable 足够,避免过度工程化。
- 需要 SSR:Pinia 对 SSR 有良好支持,自定义 composable 需注意状态隔离。
结语
Vue 3 的状态管理方案没有绝对的好坏,关键在于匹配项目需求。Pinia 凭借简洁的 API 和优秀的 TypeScript 支持,已成为大多数场景的首选。Vuex 作为经典方案,在旧项目中仍有价值。而自定义 composable 则为轻量级需求提供了灵活的选择。理解它们的差异,能帮助你在不同场景下做出更明智的决策。
未经允许不得转载:任鹏个人博客 » Vue 3 全局状态管理方案对比:Pinia、Vuex 与自定义 composable


朋友圈点赞图在线生成源码