源本科技 | 码上会

Vue3 Pinia Action

2026/04/25
27
0

引言

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         # 登录功能组件

定义模拟 API 接口

创建登录请求的模拟接口,用于模拟后端登录验证逻辑。
文件路径: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('账号或密码错误'));
    }
  });
};

定义用户状态 Store

整合 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 实例调用。