Pinia 状态管理
Pinia 是 Vue 3 官方推荐的状态管理库,比 Vuex 更简洁,完整支持 TypeScript,且支持 Composition API 风格。
为什么选 Pinia
| 特性 | Vuex 4 | Pinia |
|---|---|---|
| TypeScript 支持 | 一般 | 完整 |
| Composition API | 不支持 | 原生支持 |
| mutations | 必须 | 不需要 |
| 模块嵌套 | 复杂 | 扁平 Store |
| DevTools | 支持 | 支持 |
| 体积 | ~10KB | ~1KB |
安装与配置
ts
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')定义 Store
Option Store 风格
ts
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Eduardo'
}),
getters: {
doubled: (state) => state.count * 2,
// 使用其他 getter
doubledPlusOne(): number {
return this.doubled + 1
}
},
actions: {
increment() {
this.count++
},
async fetchUser(userId: number) {
const user = await api.getUser(userId)
this.name = user.name
}
}
})Setup Store 风格(推荐)
ts
// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
// state
const user = ref<User | null>(null)
const loading = ref(false)
const error = ref<string | null>(null)
// getters
const isLoggedIn = computed(() => user.value !== null)
const displayName = computed(() =>
user.value ? `${user.value.firstName} ${user.value.lastName}` : '游客'
)
// actions
async function login(credentials: LoginCredentials) {
loading.value = true
error.value = null
try {
user.value = await authApi.login(credentials)
} catch (e) {
error.value = (e as Error).message
} finally {
loading.value = false
}
}
function logout() {
user.value = null
authApi.logout()
}
return { user, loading, error, isLoggedIn, displayName, login, logout }
})在组件中使用
vue
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'
import { useCounterStore } from '@/stores/counter'
const userStore = useUserStore()
const counterStore = useCounterStore()
// ✅ storeToRefs:解构保持响应性(state 和 getters)
const { user, loading, isLoggedIn, displayName } = storeToRefs(userStore)
// actions 直接解构(不需要 storeToRefs)
const { login, logout } = userStore
// 直接修改 state(Pinia 允许)
function updateName() {
userStore.user!.name = 'New Name'
}
// $patch:批量修改
function batchUpdate() {
userStore.$patch({
loading: false,
error: null
})
// 或函数形式(适合复杂修改)
userStore.$patch(state => {
state.loading = false
state.error = null
})
}
// $reset:重置到初始状态(Option Store)
function reset() {
counterStore.$reset()
}
</script>
<template>
<div>
<p v-if="loading">加载中...</p>
<p v-else-if="isLoggedIn">欢迎,{{ displayName }}</p>
<button @click="logout">退出</button>
</div>
</template>Store 间通信
ts
// stores/cart.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'
export const useCartStore = defineStore('cart', () => {
const items = ref<CartItem[]>([])
// 在 action 中使用其他 store
async function checkout() {
const userStore = useUserStore() // 在函数内部调用,避免循环依赖
if (!userStore.isLoggedIn) {
throw new Error('请先登录')
}
await orderApi.create({
userId: userStore.user!.id,
items: items.value
})
items.value = []
}
return { items, checkout }
})持久化插件
ts
// pinia-plugin-persistedstate
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// 在 store 中启用
export const useAuthStore = defineStore('auth', () => {
const token = ref<string | null>(null)
return { token }
}, {
persist: {
key: 'auth',
storage: localStorage,
paths: ['token'] // 只持久化 token
}
})自定义插件
ts
// 日志插件
import type { PiniaPluginContext } from 'pinia'
function loggerPlugin({ store }: PiniaPluginContext) {
store.$subscribe((mutation, state) => {
console.log(`[${store.$id}] ${mutation.type}:`, mutation)
})
store.$onAction(({ name, args, after, onError }) => {
console.log(`[${store.$id}] action: ${name}`, args)
after(result => console.log(`[${store.$id}] result:`, result))
onError(error => console.error(`[${store.$id}] error:`, error))
})
}
pinia.use(loggerPlugin)总结
- Setup Store 风格更灵活,与 Composition API 完全一致
storeToRefs解构 state/getters 保持响应性,actions 直接解构- Pinia 允许直接修改 state,无需 mutations
- Store 间通信在 action 内部调用其他 store(避免循环依赖)
- 配合
pinia-plugin-persistedstate轻松实现状态持久化