Pinia action结果统一处理推荐用$onAction全局拦截,通过before/after/onError钩子管理loading、错误提示等;也可在action内封装runWithStatus工具函数,或由API层标准化响应,组件层按需订阅Promise结果。

Pinia 的 action 执行结果统一处理,核心在于把副作用逻辑(如 loading、错误提示、成功反馈)从具体业务 action 中抽离出来,交由统一机制接管,既保持 action 本身干净专注,又避免重复写 try/catch 或状态更新。
用 $onAction 做全局拦截
这是最推荐的方式。$onAction 是 Pinia 内置的 AOP 机制,能监听 store 中所有 action 的执行过程,无需修改每个 action 的代码。
- 在 store 创建后立即调用
$onAction,传入一个回调函数 - 回调中通过
before(隐式)、after(Promise resolve 后)、onError(throw 或 reject 时)三个钩子注入逻辑 - 可统一控制 loading 状态、记录耗时、上报错误、弹 Toast 提示等
例如统一加 loading 和错误提示:
userStore.$onAction(({ name, after, onError }) => {
userStore.loading = true
after(() => {
userStore.loading = false
})
onError((error) => {
userStore.loading = false
showNotify(error?.message || '操作失败')
})
})在 action 内部做轻量级封装
适合需要差异化处理的场景(比如某些请求要忽略错误提示,某些要特殊 loading 文案)。
- 抽一个通用的
runWithStatus工具函数,接收 action 主体和配置项 - 函数内部管理
loading、捕获错误、触发 success/error 回调
actions: {
async fetchList() {
return this.runWithStatus(
() => api.getList(),
{ loadingKey: 'listLoading', success: (res) => this.list = res }
)
},
async runWithStatus(promiseFn, opts) {
const { loadingKey = 'loading', success, error } = opts
this[loadingKey] = true
try {
const res = await promiseFn()
success?.(res)
return res
} catch (err) {
error?.(err)
throw err
} finally {
this[loadingKey] = false
}
}
}配合 API 层返回标准化响应
让接口调用本身更“可控”,减少 action 内判断负担。
- 所有 API 请求统一包装,返回结构如
{ code: 0, data: ..., message: '' } - action 中只关心
code === 0就走成功流,否则走错误流 - 可在 api 模块里集中处理 token 过期、401 跳转、500 上报等
// src/api/request.ts
export async function request<T>(config: AxiosRequestConfig): Promise<ApiResponse<T>> {
try {
const res = await axios(config)
if (res.data.code !== 0) {
throw new Error(res.data.message || '请求异常')
}
return res.data
} catch (err) {
// 统一错误分类与上报
reportApiError(err, config.url)
throw err
}
}组件层按需订阅结果
有些场景不需要全局处理,而是希望组件自己决定怎么响应 action 结果。
- action 显式返回 Promise(尤其是 async action)
- 组件中调用时直接
.then().catch(),或用await+ try/catch - 适合一次性的、强交互反馈的操作(如提交表单后跳转)
async handleSubmit() {
try {
await this.userStore.updateProfile(this.form)
this.$message.success('保存成功')
router.push('/profile')
} catch (err) {
this.$message.error(err.message)
}
}不复杂但容易忽略

















