Pinia Actions 是 Pinia Store 中定义的方法,主要用于处理异步操作、封装业务逻辑并修改 Store 中的 State 状态。作为状态更新的核心入口,Actions 可以承载接口请求、复杂计算等副作用逻辑。结合 State 与 Getters,三者构成了 Pinia 完整的状态管理体系。开发者可以在组件中调用 Action 方法,让状态变更的流程更清晰、可控且易于测试。
与 Getters 一致,Actions 可以通过 this 访问当前 Store 实例;核心区别是 Actions 支持异步编程,可以完美处理 API 请求、定时器等异步逻辑。本案例将通过完整的用户登录功能,结合 State、Getters、Actions 三大核心模块,从零实现可运行的登录示例。
我们按照规范的目录结构创建文件,保证案例的完整性与连续性:
src/
├── api/
│ └── user/
│ └── UserApi.ts # 模拟登录 API 接口
├── store/
│ └── user.ts # 用户状态管理 Store
└── views/
└── login.vue # 登录功能组件创建登录请求的模拟接口,用于模拟后端登录验证逻辑。
文件路径:src/api/user/UserApi.ts
// 模拟用户登录请求
export const login = (username: string, password: string): Promise<any> => {
return new Promise((resolve, reject) => {
// 模拟校验逻辑
if (username === 'admin' && password === '123456') {
resolve({ id: 1, name: '镜流' });
} else {
reject(new Error('账号或密码错误'));
}
});
};整合 State(存储数据)、Getters(派生数据)、Actions(修改数据 / 异步逻辑) 三大核心,完整实现用户状态管理。
文件路径:src/store/user.ts
import {defineStore} from 'pinia';
// 导入登录接口
import {login} from '@/api/user/UserApi';
export const useUserStore = defineStore('user', {
// 1. State:存储核心状态(用户信息)
state: () => ({
userInfo: null as null | { id: number; name: string }
}),
// 2. Getters:基于 State 派生数据(计算属性)
getters: {
// 判断用户是否登录
isLogin: (state) => state.userInfo != null,
// 获取用户名称,未登录时显示默认文本
userName: (state) => state.userInfo?.name || '未登录'
},
// 3. Actions:异步/同步方法,修改 State、执行业务逻辑
actions: {
// 异步登录方法
async userLogin(username: string, password: string) {
try {
// 调用登录 API
// 通过 this 修改 State
this.userInfo = await login(username, password);
return '登录成功';
} catch (error) {
console.error('登录失败:', error);
return error;
}
},
// 同步方法:退出登录
logout() {
this.userInfo = null;
}
}
});编写完整的登录页面,包含表单输入、登录 / 退出按钮、状态展示,结合 storeToRefs 保证响应式。
文件路径:src/views/login.vue
<template>
<div class="login-container">
<h2>用户登录</h2>
<!-- 登录表单 -->
<div class="form-item">
<label>账号:</label>
<input v-model="username" placeholder="请输入账号" />
</div>
<div class="form-item">
<label>密码:</label>
<input v-model="password" type="password" placeholder="请输入密码" />
</div>
<!-- 操作按钮 -->
<button @click="handleLogin" class="btn login-btn">
登录
</button>
<button @click="handleLogout" class="btn logout-btn">
退出登录
</button>
<!-- 状态展示 -->
<div class="user-info">
<p>登录状态:{{ isLogin ? '已登录' : '未登录' }}</p>
<p>当前用户:{{ userName }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/store/user';
// 获取 Store 实例
const userStore = useUserStore();
// 解构 State 和 Getters(保留响应式)
const { isLogin, userName } = storeToRefs(userStore);
// 表单数据
const username = ref('');
const password = ref('');
// 登录事件
const handleLogin = async () => {
const res = await userStore.userLogin(username.value, password.value);
alert(res);
};
// 退出登录事件
const handleLogout = () => {
userStore.logout();
alert('退出成功');
};
</script>
<style scoped>
.login-container {
width: 400px;
margin: 50px auto;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
.form-item {
margin: 15px 0;
}
input {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.btn {
padding: 8px 20px;
margin-right: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.login-btn {
background: #409eff;
color: #fff;
}
.logout-btn {
background: #f56c6c;
color: #fff;
}
.user-info {
margin-top: 20px;
line-height: 1.8;
}
</style>修改路由,追加登录
// 登录
{
path: '/login',
component: () => import('@/views/login.vue'),
name: 'login'
},State:存储原始数据(userInfo),是整个状态的数据源,仅能通过 Actions 进行修改。
Getters:基于 State 计算派生数据(isLogin/userName),自带缓存,状态更新后自动刷新。
Actions:支持 async/await 异步语法,通过 this 访问 Store 实例,是修改 State 的唯一推荐方式。
响应式解构:使用 storeToRefs 解构 State 和 Getters,避免响应式丢失,Actions 直接通过 Store 实例调用。