引言
在 Vue 3 项目中,状态管理是不可或缺的一部分。随着 Vuex 逐渐被 Pinia 取代,Pinia 凭借其更简洁的 API、更好的 TypeScript 支持和更轻量的体积,成为了 Vue 生态中的首选状态管理方案。本文将深入讲解 Pinia 的核心概念和实战技巧。
什么是 Pinia?
Pinia 是 Vue 官方推荐的状态管理库,可以看作是 Vuex 的继任者。它具有以下特点:
- 轻量级:仅 1KB 大小(压缩后)
- TypeScript 友好:完整的类型推断
- 简洁 API:基于 Composition API 的设计
- 模块化:天然支持代码分割
- DevTools 支持:集成 Vue DevTools
核心概念
Store
Store 是 Pinia 的核心,用于存储和管理应用状态。每个 Store 都是独立的,可以单独使用。
State
State 是响应式的数据源,类似于 Vue 组件中的 data。
Getters
Getters 用于计算派生状态,类似于 Vue 的 computed。
Actions
Actions 用于处理业务逻辑,可以包含异步操作,类似于 Vue 组件中的 methods。
快速上手
安装
npm install pinia
创建 Store
使用 defineStore 创建 Store:
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// State
state: () => ({
count: 0,
name: 'Pinia'
}),
// Getters
getters: {
doubleCount: (state) => state.count * 2,
fullName: (state) => `Hello, ${state.name}!`
},
// Actions
actions: {
increment() {
this.count++
},
decrement() {
this.count--
},
setCount(value) {
this.count = value
},
async fetchCount() {
const response = await fetch('/api/count')
const data = await response.json()
this.count = data.count
}
}
})
在组件中使用
xml
<template>
<div>
<h1>{{ counterStore.fullName }}</h1>
<p>Count: {{ counterStore.count }}</p>
<p>Double: {{ counterStore.doubleCount }}</p>
<button @click="counterStore.increment">+</button>
<button @click="counterStore.decrement">-</button>
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
</script>
高级用法
Store 持久化
使用 pinia-plugin-persistedstate 实现状态持久化:
npm install pinia-plugin-persistedstate
javascript
// main.js
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
javascript
// stores/user.js
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: null
}),
actions: {
login(token, userInfo) {
this.token = token
this.userInfo = userInfo
}
},
persist: true // 自动持久化到 localStorage
})
多个 Store 协作
javascript
// stores/cart.js
import { defineStore } from 'pinia'
import { useProductStore } from './product'
export const useCartStore = defineStore('cart', {
state: () => ({
items: []
}),
actions: {
addToProduct(productId, quantity) {
const productStore = useProductStore()
const product = productStore.getProductById(productId)
const existingItem = this.items.find(item => item.id === productId)
if (existingItem) {
existingItem.quantity += quantity
} else {
this.items.push({
id: productId,
name: product.name,
price: product.price,
quantity
})
}
},
get totalPrice() {
return this.items.reduce((sum, item) => {
return sum + item.price * item.quantity
}, 0)
}
}
})
组合 Store
javascript
// stores/composed.js
import { defineStore } from 'pinia'
import { useUserStore } from './user'
import { useCartStore } from './cart'
export const useCheckoutStore = defineStore('checkout', () => {
const userStore = useUserStore()
const cartStore = useCartStore()
const canCheckout = computed(() => {
return userStore.isLoggedIn && cartStore.items.length > 0
})
const checkout = async () => {
if (!canCheckout.value) {
throw new Error('Cannot checkout')
}
// 处理结账逻辑
const result = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({
userId: userStore.userInfo.id,
items: cartStore.items
})
})
return result.json()
}
return { canCheckout, checkout }
})
最佳实践
1. 合理的 Store 划分
按业务模块划分 Store,而不是按功能类型:
bash
stores/
├── user.js # 用户相关
├── product.js # 商品相关
├── cart.js # 购物车
└── order.js # 订单
2. 使用 TypeScript
typescript
interface User {
id: number
name: string
email: string
}
export const useUserStore = defineStore('user', {
state: (): { user: User | null } => ({
user: null
}),
actions: {
setUser(user: User) {
this.user = user
}
}
})
3. 避免直接修改 State
始终通过 Actions 修改状态:
scss
// ❌ 不推荐
store.count = 100
// ✅ 推荐
store.setCount(100)
4. 使用 Getters 进行派生计算
javascript
// ✅ 推荐
getters: {
activeUsers: (state) => state.users.filter(user => user.isActive)
}
// ❌ 不推荐
computed: {
activeUsers() {
return this.users.filter(user => user.isActive)
}
}
总结
Pinia 以其简洁的 API、优秀的 TypeScript 支持和轻量的体积,成为了 Vue 3 项目状态管理的首选。通过合理的 Store 划分、类型安全和最佳实践,可以构建出可维护、可扩展的状态管理方案。
关键要点:
- 使用
defineStore创建 Store - State、Getters、Actions 三大核心
- 支持持久化和多 Store 协作
- 推荐按业务模块划分 Store
- 充分利用 TypeScript 类型推断
Pinia 让 Vue 应用的状态管理变得更加简单和优雅!