Actions 方法 #
什么是 Actions? #
Actions 是 Store 中定义业务逻辑的地方,类似于组件中的 methods。与 Vuex 不同,Pinia 的 Actions 可以直接修改状态,无需通过 mutations。
定义 Actions #
基本语法 #
ts
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
},
incrementBy(amount: number) {
this.count += amount
},
reset() {
this.count = 0
}
}
})
访问 State 和 Getters #
ts
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount(): number {
return this.count * 2
}
},
actions: {
// 访问 state
increment() {
this.count++
},
// 访问 getter
incrementDouble() {
this.count = this.doubleCount
},
// 同时访问 state 和 getter
setToDouble() {
this.count = this.doubleCount
}
}
})
使用 Actions #
在组件中调用 #
vue
<template>
<div>
<p>Count: {{ counter.count }}</p>
<button @click="counter.increment">+1</button>
<button @click="counter.incrementBy(5)">+5</button>
<button @click="counter.reset">Reset</button>
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
解构使用 #
Actions 可以直接解构,无需 storeToRefs:
vue
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
// 直接解构 actions
const { increment, incrementBy, reset } = counter
// 或者使用 storeToRefs 解构 state,单独获取 actions
const { count } = storeToRefs(counter)
const { increment } = counter
</script>
异步 Actions #
Actions 天然支持异步操作:
基本异步操作 #
ts
export const useProductStore = defineStore('product', {
state: () => ({
products: [],
loading: false,
error: null as string | null
}),
actions: {
async fetchProducts() {
this.loading = true
this.error = null
try {
const response = await fetch('/api/products')
this.products = await response.json()
} catch (error) {
this.error = error.message
} finally {
this.loading = false
}
}
}
})
使用 async/await #
ts
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null as User | null,
token: null as string | null
}),
actions: {
async login(credentials: { email: string; password: string }) {
try {
const response = await api.login(credentials)
this.user = response.user
this.token = response.token
return true
} catch (error) {
console.error('Login failed:', error)
return false
}
},
async logout() {
await api.logout()
this.user = null
this.token = null
}
}
})
并行请求 #
ts
export const useDashboardStore = defineStore('dashboard', {
state: () => ({
users: [],
products: [],
orders: []
}),
actions: {
async fetchAll() {
const [users, products, orders] = await Promise.all([
api.getUsers(),
api.getProducts(),
api.getOrders()
])
this.users = users
this.products = products
this.orders = orders
}
}
})
调用其他 Actions #
同一 Store 内 #
ts
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[],
total: 0
}),
actions: {
addItem(item: CartItem) {
this.items.push(item)
this.calculateTotal()
},
removeItem(itemId: number) {
this.items = this.items.filter(item => item.id !== itemId)
this.calculateTotal()
},
calculateTotal() {
this.total = this.items.reduce((sum, item) => sum + item.price, 0)
},
clearCart() {
this.items = []
this.total = 0
}
}
})
跨 Store 调用 #
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()
const order = {
userId: userStore.user?.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 中的 Actions #
在 Setup Store 中,Actions 就是普通函数:
ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
// Actions 就是普通函数
function increment() {
count.value++
}
function incrementBy(amount: number) {
count.value += amount
}
async function fetchInitialValue() {
const response = await fetch('/api/initial-value')
count.value = await response.json()
}
return {
count,
increment,
incrementBy,
fetchInitialValue
}
})
订阅 Actions #
$onAction #
监听 action 的调用:
ts
const counterStore = useCounterStore()
counterStore.$onAction(({
name, // action 名称
args, // action 参数
after, // action 成功后的钩子
onError // action 出错时的钩子
}) => {
console.log(`Action ${name} called with args:`, args)
after((result) => {
console.log(`Action ${name} finished with result:`, result)
})
onError((error) => {
console.error(`Action ${name} failed with error:`, error)
})
})
实际应用示例 #
ts
// 日志记录
const userStore = useUserStore()
userStore.$onAction(({ name, args, after, onError }) => {
const startTime = Date.now()
console.log(`[Action] ${name} started`)
after(() => {
const duration = Date.now() - startTime
console.log(`[Action] ${name} completed in ${duration}ms`)
})
onError((error) => {
console.error(`[Action] ${name} failed:`, error)
})
})
移除订阅 #
ts
const unsubscribe = userStore.$onAction(() => {
// ...
})
// 移除订阅
unsubscribe()
错误处理 #
Try-Catch #
ts
export const useProductStore = defineStore('product', {
state: () => ({
products: [],
loading: false,
error: null as string | null
}),
actions: {
async fetchProducts() {
this.loading = true
this.error = null
try {
const response = await fetch('/api/products')
if (!response.ok) {
throw new Error('Failed to fetch products')
}
this.products = await response.json()
} catch (error) {
this.error = error instanceof Error ? error.message : 'Unknown error'
throw error // 可以选择重新抛出
} finally {
this.loading = false
}
}
}
})
全局错误处理 #
ts
// main.ts
const pinia = createPinia()
pinia.use(({ store }) => {
store.$onAction(({ name, onError }) => {
onError((error) => {
console.error(`Error in ${store.$id}.${name}:`, error)
// 可以发送到错误追踪服务
// trackError(error)
})
})
})
实际示例 #
用户认证 Store #
ts
// stores/auth.ts
import { defineStore } from 'pinia'
import { api } from '@/api'
interface User {
id: number
email: string
name: string
role: string
}
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null as User | null,
token: localStorage.getItem('token'),
loading: false,
error: null as string | null
}),
getters: {
isAuthenticated: (state) => !!state.token && !!state.user,
isAdmin: (state) => state.user?.role === 'admin'
},
actions: {
async login(email: string, password: string) {
this.loading = true
this.error = null
try {
const response = await api.login({ email, password })
this.token = response.token
this.user = response.user
localStorage.setItem('token', response.token)
return true
} catch (error) {
this.error = error instanceof Error ? error.message : 'Login failed'
return false
} finally {
this.loading = false
}
},
async register(data: { email: string; password: string; name: string }) {
this.loading = true
this.error = null
try {
const response = await api.register(data)
this.token = response.token
this.user = response.user
localStorage.setItem('token', response.token)
return true
} catch (error) {
this.error = error instanceof Error ? error.message : 'Registration failed'
return false
} finally {
this.loading = false
}
},
async fetchUser() {
if (!this.token) return
try {
const user = await api.getCurrentUser()
this.user = user
} catch (error) {
this.logout()
}
},
logout() {
this.user = null
this.token = null
localStorage.removeItem('token')
},
clearError() {
this.error = null
}
}
})
数据获取 Store #
ts
// stores/product.ts
import { defineStore } from 'pinia'
import { api } from '@/api'
interface Product {
id: number
name: string
price: number
description: string
category: string
stock: number
}
export const useProductStore = defineStore('product', {
state: () => ({
products: [] as Product[],
currentProduct: null as Product | null,
loading: false,
error: null as string | null,
filters: {
category: null as string | null,
minPrice: null as number | null,
maxPrice: null as number | null
}
}),
getters: {
filteredProducts: (state) => {
let result = state.products
if (state.filters.category) {
result = result.filter(p => p.category === state.filters.category)
}
if (state.filters.minPrice !== null) {
result = result.filter(p => p.price >= state.filters.minPrice!)
}
if (state.filters.maxPrice !== null) {
result = result.filter(p => p.price <= state.filters.maxPrice!)
}
return result
}
},
actions: {
async fetchProducts() {
this.loading = true
this.error = null
try {
this.products = await api.getProducts()
} catch (error) {
this.error = error instanceof Error ? error.message : 'Failed to fetch products'
} finally {
this.loading = false
}
},
async fetchProduct(id: number) {
this.loading = true
this.error = null
try {
this.currentProduct = await api.getProduct(id)
} catch (error) {
this.error = error instanceof Error ? error.message : 'Failed to fetch product'
} finally {
this.loading = false
}
},
async createProduct(product: Omit<Product, 'id'>) {
this.loading = true
this.error = null
try {
const newProduct = await api.createProduct(product)
this.products.push(newProduct)
return newProduct
} catch (error) {
this.error = error instanceof Error ? error.message : 'Failed to create product'
return null
} finally {
this.loading = false
}
},
async updateProduct(id: number, updates: Partial<Product>) {
this.loading = true
this.error = null
try {
const updated = await api.updateProduct(id, updates)
const index = this.products.findIndex(p => p.id === id)
if (index !== -1) {
this.products[index] = updated
}
if (this.currentProduct?.id === id) {
this.currentProduct = updated
}
return updated
} catch (error) {
this.error = error instanceof Error ? error.message : 'Failed to update product'
return null
} finally {
this.loading = false
}
},
async deleteProduct(id: number) {
this.loading = true
this.error = null
try {
await api.deleteProduct(id)
this.products = this.products.filter(p => p.id !== id)
if (this.currentProduct?.id === id) {
this.currentProduct = null
}
return true
} catch (error) {
this.error = error instanceof Error ? error.message : 'Failed to delete product'
return false
} finally {
this.loading = false
}
},
setFilters(filters: Partial<typeof this.filters>) {
this.filters = { ...this.filters, ...filters }
},
clearFilters() {
this.filters = {
category: null,
minPrice: null,
maxPrice: null
}
}
}
})
Actions 最佳实践 #
1. 单一职责 #
ts
// 推荐:每个 action 做一件事
actions: {
async fetchProducts() { /* ... */ },
async createProduct(product) { /* ... */ },
async updateProduct(id, updates) { /* ... */ },
async deleteProduct(id) { /* ... */ }
}
// 不推荐:一个 action 做多件事
actions: {
async handleProducts(action, data) { /* ... */ }
}
2. 错误处理 #
ts
// 推荐:完善的错误处理
actions: {
async fetchData() {
this.loading = true
this.error = null
try {
const data = await api.getData()
this.data = data
} catch (error) {
this.error = error.message
throw error
} finally {
this.loading = false
}
}
}
3. 返回值 #
ts
// 推荐:返回有意义的结果
actions: {
async login(credentials) {
try {
const response = await api.login(credentials)
this.user = response.user
return { success: true, user: response.user }
} catch (error) {
return { success: false, error: error.message }
}
}
}
下一步 #
现在你已经掌握了 Actions 的使用,接下来让我们学习组合式 API 风格的 Store。
- Setup Store - 组合式 API 风格
最后更新:2026-03-28