You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3/Pinia(TypeScript):如何为独立Action处理数据、错误与加载状态

Pinia+TypeScript实现Action独立状态管理(Vue3)

问题核心

需要为每个Store Action单独维护loading、error状态,避免全局状态冲突,同时严格遵守TypeScript的类型约束(不能随意修改User接口结构)。

解决方案1:为单个Action单独声明状态

直接在Store的state中为getMe Action专属添加加载和错误状态,结构清晰,适合少量Action的场景。

修改Auth Store代码

import { defineStore } from "pinia";
import AuthService from "@/api/modules/auth";

interface User {
  email: string;
  first_name: string;
  last_name: string;
  force_password_change: boolean;
  groups: string[];
  has_2fa_enabled: boolean;
  is_staff: boolean;
  lang: string;
  last_password_change: string;
  permissions: string[];
  session_expiry_date: string;
  uid: string;
}

export const useAuthStore = defineStore({
  id: "auth",
  state: () => ({
    user: {} as User,
    // 为getMe单独设置状态,避免与其他Action冲突
    getMeLoading: false,
    getMeError: null as Error | null,
  }),
  actions: {
    async getMe() {
      // 初始化状态
      this.getMeLoading = true;
      this.getMeError = null;
      try {
        const userData = await AuthService.getMe();
        this.user = userData;
        return userData;
      } catch (error) {
        // 捕获并存储错误
        this.getMeError = error as Error;
        throw error; // 可选:让组件也能捕获错误做后续处理
      } finally {
        // 请求结束后无论成功失败都关闭加载状态
        this.getMeLoading = false;
      }
    },
  },
});

组件中使用(App.vue)

<script setup lang="ts">
import { useAuthStore } from "@/stores";
import { storeToRefs } from "pinia";

const userStore = useAuthStore();
// 直接解构获取Action专属状态
const { user, getMeLoading, getMeError } = storeToRefs(userStore);

// 发起请求并处理状态
const fetchUser = async () => {
  try {
    await userStore.getMe();
    console.log("用户数据:", user.value);
  } catch (err) {
    console.error("获取用户失败:", getMeError.value);
  }
};

fetchUser();
</script>

解决方案2:统一管理多Action状态(扩展性更强)

如果Store中有多个Action需要独立状态,用一个对象统一管理,更易维护和扩展。

修改Auth Store代码

import { defineStore } from "pinia";
import AuthService from "@/api/modules/auth";

interface User {
  email: string;
  first_name: string;
  last_name: string;
  force_password_change: boolean;
  groups: string[];
  has_2fa_enabled: boolean;
  is_staff: boolean;
  lang: string;
  last_password_change: string;
  permissions: string[];
  session_expiry_date: string;
  uid: string;
}

// 定义Action状态的通用接口
interface ActionState {
  loading: boolean;
  error: Error | null;
}

export const useAuthStore = defineStore({
  id: "auth",
  state: () => ({
    user: {} as User,
    // 用对象统一管理所有Action的状态
    actionStates: {
      getMe: { loading: false, error: null } as ActionState,
      // 可添加其他Action的状态,比如login/logout等
    } as Record<string, ActionState>,
  }),
  getters: {
    // 封装getters方便组件直接获取
    getMeLoading: (state) => state.actionStates.getMe.loading,
    getMeError: (state) => state.actionStates.getMe.error,
  },
  actions: {
    async getMe() {
      const stateKey = "getMe";
      this.actionStates[stateKey].loading = true;
      this.actionStates[stateKey].error = null;
      try {
        const userData = await AuthService.getMe();
        this.user = userData;
        return userData;
      } catch (error) {
        this.actionStates[stateKey].error = error as Error;
        throw error;
      } finally {
        this.actionStates[stateKey].loading = false;
      }
    },
    // 其他Action示例:login
    async login(credentials: { email: string; password: string }) {
      const stateKey = "login";
      if (!this.actionStates[stateKey]) {
        // 初始化未定义的Action状态
        this.actionStates[stateKey] = { loading: false, error: null };
      }
      this.actionStates[stateKey].loading = true;
      this.actionStates[stateKey].error = null;
      try {
        // 登录逻辑
        // const result = await AuthService.login(credentials);
        // ...
      } catch (error) {
        this.actionStates[stateKey].error = error as Error;
        throw error;
      } finally {
        this.actionStates[stateKey].loading = false;
      }
    },
  },
});

组件中使用

<script setup lang="ts">
import { useAuthStore } from "@/stores";
import { storeToRefs } from "pinia";

const userStore = useAuthStore();
// 通过getters直接获取状态
const { user, getMeLoading, getMeError } = storeToRefs(userStore);

const fetchUser = async () => {
  try {
    await userStore.getMe();
    console.log("用户数据:", user.value);
  } catch (err) {
    console.error("获取用户失败:", getMeError.value);
  }
};

fetchUser();
</script>

额外优化:完善Service的类型约束

避免any类型,明确返回值类型让TypeScript更好地发挥作用:

import { Api } from "../apiSettings";

class AuthService {
  // 明确返回Promise<User>,而非any
  async getMe(): Promise<User> {
    const response = await Api.get("api/auth/me/");
    // 根据实际API响应结构调整,假设返回数据在response.data中
    return response.data as User;
  }
}

export default new AuthService();

内容的提问来源于stack exchange,提问作者Jer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:55:16