Pinia
Action 与异步操作
掌握在 Pinia actions 中处理异步请求、调用其他 action、管理加载状态等实战技巧。
🎯 引言
学会这篇文章,你能在 Pinia 的 actions 中处理接口请求等异步操作。我们会用一个最简例子讲清楚核心流程,并解释 action 中 this 到底指向哪里。
🧱 为什么异步要放到 action 里
假设你要从后台拉取用户信息,然后显示在多个地方。如果每个组件都自己发请求:
- 代码会重复。
- 加载状态和错误处理分散。
- 组件会变得很臃肿。
把请求放到 Store 的 action 中,组件只需要调用一个方法,剩下的交给 Store。
🛠 在 action 中发请求
Pinia 的 action 支持 async/await,写法很自然:
stores/user.js
import { defineStore } from 'pinia';
import { fetchUserInfo } from '../api/user.js';
export const useUserStore = defineStore('user', {
state: () => ({
info: null,
loading: false,
}),
actions: {
async loadUser() {
this.loading = true;
try {
const res = await fetchUserInfo();
this.info = res.data;
} finally {
this.loading = false;
}
},
},
});
组件中使用:
App.vue
<template>
<p v-if="user.loading">加载中...</p>
<p v-else>用户名:{{ user.info?.name }}</p>
<button @click="user.loadUser">获取用户</button>
</template>
<script setup>
import { useUserStore } from './stores/user.js';
const user = useUserStore();
</script>
action 里用
this.info 和 this.loading 读写 state。这里的 this 指向当前 Store 实例,不是组件实例。⚡ action 里调用其他 action
一个 action 可以调用同一个 Store 里的其他 action:
stores/counter.js
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
add() {
this.count++;
},
async addAndLog() {
this.add(); // 调用本 Store 的 action
console.log('当前计数:', this.count);
},
},
});
调用本 Store 的 action 直接用
this.xxx(),和调用普通方法一样。💡 跨 Store 调用
有时候一个 action 需要用到另一个 Store 的数据或方法。比如在登录成功后刷新购物车:
stores/auth.js
import { defineStore } from 'pinia';
import { useCartStore } from './cart.js';
export const useAuthStore = defineStore('auth', {
actions: {
async login(form) {
const res = await apiLogin(form);
this.token = res.token;
const cart = useCartStore();
await cart.loadCart();
},
},
});
在 action 内部调用
useCartStore() 是安全的,因为 Pinia 已经注册好了。但不要在 Store 文件的最外层直接调用 use 函数。🧾 小节总结
- 异步请求建议放到 Store 的 action 中,组件只负责触发和展示。
- action 中
this指向 Store 实例,用this.xxx读写 state。 - action 支持
async/await。 - 一个 action 可以调用本 Store 的其他 action,也可以调用其他 Store 的 action。
❓ 知识问答
Q1:action 里的 this 指向组件吗?
不指向组件。this 指向当前的 Store 实例,所以你可以用 this.stateName 和 this.actionName()。
Q2:action 中调用其他 Store 的 action 会创建新实例吗?
不会。useXxxStore() 返回的是单例,多次调用拿到的是同一个实例。
Q3:可以在 action 里直接修改其他 Store 的 state 吗?
技术上可以,但不推荐。应该让那个 Store 自己提供 action 来修改自己的状态,这样职责更清晰。
Q4:action 一定要是异步的吗?
不一定。同步逻辑也可以放到 action 中,只是异步场景更需要它。
🧪 小练习
补全下面的 useArticleStore,实现:
articles初始为空数组,loading为false。fetchArticles()action 先设置loading为true,1 秒后把模拟数据放入articles,再把loading设为false。
stores/article.js
import { defineStore } from 'pinia';
function mockFetch() {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ id: 1, title: 'Pinia 入门' },
{ id: 2, title: 'Vue3 新特性' },
]);
}, 1000);
});
}
export const useArticleStore = defineStore('article', {
state: () => ({
// 请在这里定义初始状态
}),
actions: {
async fetchArticles() {
// 请在这里编写代码
},
},
});
🎉 恭喜你掌握了 Pinia 的异步 action!下一篇我们学习多 Store 组织、TypeScript 和插件扩展。
