以下是几个 Pinia 的应用案例,帮助你理解如何在 Vue 3 项目中使用 Pinia 进行状态管理。
案例 1:用户认证状态管理
场景:管理用户的登录状态、用户信息,并提供登录和退出功能。
// stores/auth.ts
import { defineStore } from 'pinia'
interface User {
id: number
name: string
email: string
}
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null as User | null,
isAuthenticated: false,
}),
actions: {
async login(email: string, password: string) {
// 模拟 API 调用
const response = await fakeApiLogin(email, password)
this.user = response.user
this.isAuthenticated = true
},
logout() {
this.user = null
this.isAuthenticated = false
},
},
getters: {
userName: (state) => state.user?.name || 'Guest',
},
})
在组件中使用
<script setup>
import { useAuthStore } from '@/stores/auth'
const auth = useAuthStore()
// 登录操作
const handleLogin = async () => {
await auth.login('user@example.com', 'password')
console.log(auth.userName) // 输出用户名称
}
</script>
案例 2:购物车管理
场景:管理用户的购物车商品、总价和操作。
// stores/cart.ts
import { defineStore } from 'pinia'
interface Product {
id: number
name: string
price: number
}
interface CartItem extends Product {
quantity: number
}
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[],
}),
getters: {
totalPrice: (state) =>
state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
},
actions: {
addToCart(product: Product) {
const existing = this.items.find((item) => item.id === product.id)
if (existing) {
existing.quantity++
} else {
this.items.push({ ...product, quantity: 1 })
}
},
removeItem(productId: number) {
this.items = this.items.filter((item) => item.id !== productId)
},
clearCart() {
this.items = []
},
},
})
在组件中使用:
<template>
<div v-for="item in cart.items" :key="item.id">
{{ item.name }} × {{ item.quantity }}
</div>
<p>总价: {{ cart.totalPrice }}</p>
<button @click="cart.addToCart(newProduct)">添加商品</button>
</template>
<script setup>
import { useCartStore } from '@/stores/cart'
const cart = useCartStore()
const newProduct = { id: 1, name: '商品A', price: 100 }
</script>
案例 3:全局主题切换
场景:管理应用的主题(亮色/暗色模式)。
// stores/theme.ts
import { defineStore } from 'pinia'
export const useThemeStore = defineStore('theme', {
state: () => ({
isDarkMode: false,
}),
actions: {
toggleTheme() {
this.isDarkMode = !this.isDarkMode
// 可选:保存到 localStorage
localStorage.setItem('theme', this.isDarkMode ? 'dark' : 'light')
},
},
})
在组件中使用:
<template>
<button @click="theme.toggleTheme">
{{ theme.isDarkMode ? '切换到亮色' : '切换到暗色' }}
</button>
</template>
<script setup>
import { useThemeStore } from '@/stores/theme'
const theme = useThemeStore()
</script>
案例 4:异步数据加载
场景:管理 API 数据的加载状态和错误处理。
// stores/posts.ts
import { defineStore } from 'pinia'
interface Post {
id: number
title: string
content: string
}
export const usePostStore = defineStore('posts', {
state: () => ({
posts: [] as Post[],
isLoading: false,
error: null as string | null,
}),
actions: {
async fetchPosts() {
this.isLoading = true
try {
const response = await fetch('https://api.example.com/posts')
this.posts = await response.json()
} catch (error) {
this.error = '加载失败'
} finally {
this.isLoading = false
}
},
},
})
在组件中使用:
<template>
<div v-if="posts.isLoading">加载中...</div>
<div v-else-if="posts.error">{{ posts.error }}</div>
<div v-else v-for="post in posts.posts" :key="post.id">
{{ post.title }}
</div>
</template>
<script setup>
import { usePostStore } from '@/stores/posts'
const posts = usePostStore()
onMounted(() => posts.fetchPosts())
</script>
高级用法:组合 Store
将多个 Store 组合使用,例如在用户登录后加载购物车数据:
// 组合使用 AuthStore 和 CartStore
import { useAuthStore } from '@/stores/auth'
import { useCartStore } from '@/stores/cart'
const auth = useAuthStore()
const cart = useCartStore()
// 用户登录后加载购物车
watch(
() => auth.isAuthenticated,
(isAuthenticated) => {
if (isAuthenticated) {
cart.loadCartData(auth.user.id)
}
}
)
总结
Pinia 的核心优势:
- 类型安全:天然支持 TypeScript。
- 模块化:通过多个 Store 管理不同功能。
- 灵活组合:可在组件或 Store 之间复用逻辑。
- 轻量简洁:API 设计简单,学习成本低。
适用于需要全局状态管理的场景,如用户信息、主题、购物车、API 数据缓存等。