Store 组合 #
概述 #
在实际应用中,我们经常需要在多个 Store 之间共享状态或调用彼此的方法。Pinia 提供了多种方式来实现 Store 之间的组合和协作。
访问其他 Store #
在 Getters 中访问 #
ts
// stores/user.ts
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
id: 1,
name: 'John',
email: 'john@example.com'
})
})
ts
// stores/cart.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[]
}),
getters: {
summary(): string {
const userStore = useUserStore()
return `${userStore.name} 的购物车有 ${this.items.length} 件商品`
},
// 返回用户专属的购物车标识
cartId(): string {
const userStore = useUserStore()
return `cart-${userStore.id}`
}
}
})
在 Actions 中访问 #
ts
// stores/order.ts
import { defineStore } from 'pinia'
import { useCartStore } from './cart'
import { useUserStore } from './user'
export const useOrderStore = defineStore('order', {
state: () => ({
orders: [] as Order[]
}),
actions: {
async createOrder() {
const cartStore = useCartStore()
const userStore = useUserStore()
// 验证用户是否登录
if (!userStore.id) {
throw new Error('请先登录')
}
// 验证购物车是否为空
if (cartStore.items.length === 0) {
throw new Error('购物车为空')
}
const order = {
userId: userStore.id,
items: [...cartStore.items],
total: cartStore.total,
createdAt: new Date()
}
const response = await api.createOrder(order)
this.orders.push(response)
// 清空购物车
cartStore.clearCart()
return response
}
}
})
Setup Store 中访问 #
ts
// stores/dashboard.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useUserStore } from './user'
import { useCartStore } from './cart'
import { useOrderStore } from './order'
export const useDashboardStore = defineStore('dashboard', () => {
const userStore = useUserStore()
const cartStore = useCartStore()
const orderStore = useOrderStore()
const loading = ref(false)
// 组合多个 store 的数据
const summary = computed(() => ({
userName: userStore.name,
cartItems: cartStore.itemCount,
orderCount: orderStore.orders.length
}))
// 组合多个 store 的方法
async function loadAll() {
loading.value = true
try {
await Promise.all([
userStore.fetchUser(),
cartStore.fetchCart(),
orderStore.fetchOrders()
])
} finally {
loading.value = false
}
}
return {
loading,
summary,
loadAll
}
})
共享状态 #
使用外部状态 #
多个 Store 可以共享同一个外部状态:
ts
// stores/shared.ts
import { ref } from 'vue'
// 共享的主题状态
export const sharedTheme = ref<'light' | 'dark'>('light')
export const sharedLocale = ref('zh-CN')
ts
// stores/user.ts
import { defineStore } from 'pinia'
import { sharedTheme, sharedLocale } from './shared'
export const useUserStore = defineStore('user', () => {
const theme = sharedTheme
const locale = sharedLocale
function setTheme(newTheme: 'light' | 'dark') {
theme.value = newTheme
}
return { theme, locale, setTheme }
})
ts
// stores/admin.ts
import { defineStore } from 'pinia'
import { sharedTheme, sharedLocale } from './shared'
export const useAdminStore = defineStore('admin', () => {
const theme = sharedTheme
const locale = sharedLocale
return { theme, locale }
})
使用 provide/inject #
在组件树中共享状态:
ts
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
// 提供全局状态
app.provide('globalConfig', {
apiBaseUrl: 'https://api.example.com',
version: '1.0.0'
})
ts
// stores/config.ts
import { defineStore } from 'pinia'
import { inject } from 'vue'
export const useConfigStore = defineStore('config', () => {
const config = inject('globalConfig') as {
apiBaseUrl: string
version: string
}
return { config }
})
Store 组合模式 #
1. 扇出模式(Fan-out) #
一个 Store 调用多个其他 Store:
ts
// stores/app.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'
import { useCartStore } from './cart'
import { useNotificationStore } from './notification'
export const useAppStore = defineStore('app', () => {
const userStore = useUserStore()
const cartStore = useCartStore()
const notificationStore = useNotificationStore()
async function initialize() {
await userStore.fetchUser()
await cartStore.fetchCart()
notificationStore.show('应用初始化完成')
}
async function logout() {
await userStore.logout()
cartStore.clearCart()
notificationStore.show('已退出登录')
}
return { initialize, logout }
})
2. 扇入模式(Fan-in) #
多个 Store 调用同一个 Store:
ts
// stores/auth.ts
import { defineStore } from 'pinia'
export const useAuthStore = defineStore('auth', {
state: () => ({
token: null as string | null
}),
getters: {
isAuthenticated: (state) => !!state.token
}
})
ts
// stores/user.ts
import { defineStore } from 'pinia'
import { useAuthStore } from './auth'
export const useUserStore = defineStore('user', {
actions: {
async fetchUser() {
const authStore = useAuthStore()
if (!authStore.isAuthenticated) {
throw new Error('未授权')
}
// ...
}
}
})
ts
// stores/order.ts
import { defineStore } from 'pinia'
import { useAuthStore } from './auth'
export const useOrderStore = defineStore('order', {
actions: {
async fetchOrders() {
const authStore = useAuthStore()
if (!authStore.isAuthenticated) {
throw new Error('未授权')
}
// ...
}
}
})
3. 链式模式 #
Store 之间形成调用链:
ts
// stores/product.ts
export const useProductStore = defineStore('product', {
state: () => ({ products: [] }),
actions: {
async fetchProducts() {
// ...
}
}
})
ts
// stores/cart.ts
import { useProductStore } from './product'
export const useCartStore = defineStore('cart', {
actions: {
async addToCart(productId: number) {
const productStore = useProductStore()
const product = productStore.products.find(p => p.id === productId)
// ...
}
}
})
ts
// stores/order.ts
import { useCartStore } from './cart'
export const useOrderStore = defineStore('order', {
actions: {
async createOrder() {
const cartStore = useCartStore()
// 使用购物车数据创建订单
// ...
}
}
})
避免循环依赖 #
问题场景 #
ts
// stores/a.ts
import { useBStore } from './b'
export const useAStore = defineStore('a', {
actions: {
doSomething() {
const bStore = useBStore() // 依赖 B
}
}
})
// stores/b.ts
import { useAStore } from './a'
export const useBStore = defineStore('b', {
actions: {
doSomethingElse() {
const aStore = useAStore() // 依赖 A - 循环依赖!
}
}
})
解决方案 #
1. 提取共享逻辑 #
ts
// stores/shared.ts
export function sharedLogic() {
// 共享的业务逻辑
}
ts
// stores/a.ts
import { sharedLogic } from './shared'
export const useAStore = defineStore('a', {
actions: {
doSomething() {
sharedLogic()
}
}
})
2. 使用延迟引用 #
ts
// stores/a.ts
export const useAStore = defineStore('a', {
actions: {
doSomething() {
// 在函数内部引用,而不是模块顶层
const { useBStore } = require('./b')
const bStore = useBStore()
}
}
})
3. 重构 Store 结构 #
ts
// stores/common.ts
export const useCommonStore = defineStore('common', {
state: () => ({
sharedData: null
})
})
ts
// stores/a.ts
import { useCommonStore } from './common'
export const useAStore = defineStore('a', {
actions: {
doSomething() {
const commonStore = useCommonStore()
}
}
})
ts
// stores/b.ts
import { useCommonStore } from './common'
export const useBStore = defineStore('b', {
actions: {
doSomethingElse() {
const commonStore = useCommonStore()
}
}
})
实际示例:电商应用 #
ts
// stores/user.ts
import { defineStore } from 'pinia'
interface User {
id: number
name: string
email: string
}
export const useUserStore = defineStore('user', {
state: () => ({
user: null as User | null,
token: null as string | null
}),
getters: {
isAuthenticated: (state) => !!state.token,
displayName: (state) => state.user?.name || 'Guest'
},
actions: {
async login(email: string, password: string) {
const response = await api.login({ email, password })
this.user = response.user
this.token = response.token
},
logout() {
this.user = null
this.token = null
}
}
})
ts
// stores/product.ts
import { defineStore } from 'pinia'
interface Product {
id: number
name: string
price: number
stock: number
}
export const useProductStore = defineStore('product', {
state: () => ({
products: [] as Product[],
loading: false
}),
actions: {
async fetchProducts() {
this.loading = true
try {
this.products = await api.getProducts()
} finally {
this.loading = false
}
}
}
})
ts
// stores/cart.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'
import { useProductStore } from './product'
interface CartItem {
productId: number
quantity: number
}
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[]
}),
getters: {
total(): number {
const productStore = useProductStore()
return this.items.reduce((sum, item) => {
const product = productStore.products.find(p => p.id === item.productId)
return sum + (product?.price || 0) * item.quantity
}, 0)
}
},
actions: {
addItem(productId: number, quantity: number = 1) {
const productStore = useProductStore()
const product = productStore.products.find(p => p.id === productId)
if (!product) throw new Error('商品不存在')
if (product.stock < quantity) throw new Error('库存不足')
const existingItem = this.items.find(item => item.productId === productId)
if (existingItem) {
existingItem.quantity += quantity
} else {
this.items.push({ productId, quantity })
}
},
clearCart() {
this.items = []
}
}
})
ts
// stores/order.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'
import { useCartStore } from './cart'
interface Order {
id: number
userId: number
items: any[]
total: number
status: string
}
export const useOrderStore = defineStore('order', {
state: () => ({
orders: [] as Order[]
}),
actions: {
async createOrder() {
const userStore = useUserStore()
const cartStore = useCartStore()
if (!userStore.isAuthenticated) {
throw new Error('请先登录')
}
if (cartStore.items.length === 0) {
throw new Error('购物车为空')
}
const order = await api.createOrder({
userId: userStore.user!.id,
items: cartStore.items,
total: cartStore.total
})
this.orders.push(order)
cartStore.clearCart()
return order
},
async fetchOrders() {
const userStore = useUserStore()
if (!userStore.isAuthenticated) return
this.orders = await api.getOrders(userStore.user!.id)
}
}
})
最佳实践 #
1. 单向依赖 #
尽量保持单向依赖关系:
text
User Store ← Cart Store ← Order Store
2. 在 Actions 中引用 #
避免在 Store 定义顶层引用其他 Store:
ts
// 推荐
export const useCartStore = defineStore('cart', {
actions: {
checkout() {
const userStore = useUserStore() // 在 action 内引用
}
}
})
// 不推荐
const userStore = useUserStore() // 顶层引用可能导致问题
export const useCartStore = defineStore('cart', { /* ... */ })
3. 使用 TypeScript 类型 #
ts
// types/stores.ts
export interface UserStore {
user: User | null
token: string | null
login: (email: string, password: string) => Promise<void>
logout: () => void
}
下一步 #
现在你已经掌握了 Store 组合的使用,接下来让我们学习 Pinia 的高级特性。
- 插件系统 - 扩展 Pinia 功能
最后更新:2026-03-28