如何在 Pinia 组合式 API 中正确 await 异步操作并获取响应数据

1次阅读

如何在 Pinia 组合式 API 中正确 await 异步操作并获取响应数据

本文详解 Pinia 组合式 API(setup store)中异步动作的正确 await 方式,重点解决因错误解构 ref 导致的响应性丢失、undefined 返回值及 promise 未正确等待等问题,并提供可直接落地的修复方案与最佳实践。

本文详解 pinia 组合式 api(setup store)中异步动作的正确 await 方式,重点解决因错误解构 `ref` 导致的响应性丢失、`undefined` 返回值及 promise 未正确等待等问题,并提供可直接落地的修复方案与最佳实践。

在使用 Pinia 的组合式 API(即 defineStore 配合 setup() 函数)时,一个常见但极易被忽视的陷阱是:对 store 内部 ref 响应式变量进行对象解构(如 const { widgets } = dashboardStore),会破坏其响应性连接,导致后续读取为 undefined 或无法反映最新状态。这正是你遇到 values is undefined 错误的根本原因——getWidgets() 中解构出的 widgets 已不再是响应式引用,且 loadWidgets() 返回的是 useFetch 的 Promise 包装对象,而非实际数据;而你又试图在 .then() 中访问已失效的 widgets.value。

✅ 正确做法:保持响应式引用 + 显式等待 fetch 完成

首先,修正 store 中 loadWidgets 的实现:useFetch 默认返回一个包含 data、pending、Error 等属性的对象,它本身不自动 resolve 数据。你需要显式 await 其内部的 data(或通过 execute() 触发请求并等待完成)。同时,避免解构 ref 变量。

✅ 修复后的 dashboard.js(store)

import { defineStore } from "pinia"; import { useFetch } from "#app"; import { useAuthStore } from "./auth";  const baseUrl = import.meta.env.VITE_API_KEY + "/hr/widgets/dashboard";  export const useDashboardStore = defineStore("dashboard", () => {   const authStore = useAuthStore();   const { getToken } = authStore;    const widgets = ref([]);   const fetchedWidgets = ref(false);    // ✅ 改为返回一个可 await 的 Promise,显式 resolve 响应数据   async function loadWidgets() {     const { data, error, execute } = useFetch(baseUrl + "/list/get", {       headers: {         Accept: "application/json",         Authorization: "Bearer " + getToken,       },       // ❌ 移除 onResponse —— 它是副作用钩子,不参与 Promise 流程       // ✅ 改用 execute() + await data 实现可控等待     });      try {       await execute(); // ✅ 触发请求并等待完成       if (error.value) throw error.value;        widgets.value = data.value || [];       fetchedWidgets.value = true;       return widgets.value; // ✅ 明确返回处理后的数据     } catch (err) {       throw showError({         statusCode: 401,         statusMessage: "Error: - " + (err?.message || "Unknown error"),         fatal: true,       });     }   }    return {     widgets,          // ✅ 直接返回 ref,不在此处解构     loadWidgets,     fetchedWidgets,   // ✅ 同上   }; });

✅ 修复后的组件逻辑(<script setup>)</script>

<script setup> import { ref, onMounted } from "vue"; import { useDashboardStore } from "~/stores/dashboard"; import { useCandidatesStore } from "~/stores/candidates";  const props = defineProps({   page: {     type: String,     required: true,   }, });  const dashboardStore = useDashboardStore(); const candidatesStore = useCandidatesStore();  // ✅ 使用 ref 存储 layout/index(确保响应式) const layout = ref([]); const index = ref(0);  onMounted(async () => {   try {     const values = await getWidgets(); // ✅ 直接 await,无需 .then()     mapWidgetsData(values);   } catch (err) {     console.error("Failed to load widgets:", err);   } });  const getWidgets = async () => {   switch (props.page) {     case "dashboard": {       // ✅ 关键:不使用解构!直接通过 store 实例访问 ref       if (!dashboardStore.fetchedWidgets.value) {         return await dashboardStore.loadWidgets(); // ✅ await 返回的数据       }       return dashboardStore.widgets.value; // ✅ 访问 .value 获取当前值     }     case "employees": {       // 同理,使用 candidatesStore.widgets / .fetchedWidgets / .loadWidgets()       const { loadWidgets, widgets, fetchedWidgets } = candidatesStore;       // ⚠️ 注意:此处若仍解构,同样会丢失响应性!应改为:       // if (!candidatesStore.fetchedWidgets.value) { ... }       // return candidatesStore.widgets.value;       break;     }     default:       return [];   } };  const mapWidgetsData = (values) => {   if (!values || !Array.isArray(values.data)) return;    const itemsData = values.data;   layout.value = [];   itemsData.forEach((value) => {     try {       const position = JSON.parse(value.position);       layout.value.push({         ...position,         i: value.id,         type: value.type,       });     } catch (e) {       console.warn("Invalid position JSON for widget", value.id, e);     }   });   index.value = values.last_index + 1; }; </script>

? 关键要点总结

  • 禁止解构 ref:const { widgets } = store 会剥离响应性,必须始终通过 store.widgets.value 访问;
  • useFetch 不是“即用型” Promise:它返回的是一个包含 execute() 方法的对象,需显式调用 await execute() 并检查 data.value;
  • onResponse 是副作用钩子,非控制流节点:它在响应到达时同步执行,但不会影响外部 Promise 的 resolve 时间点,故不适合作为 await 的依据;
  • 错误处理需统一:建议在 store 层捕获并抛出业务错误,在组件层用 try/catch 处理,避免静默失败;
  • Nuxt 3 兼容性提示:useFetch 在服务端渲染(SSR)中默认惰性执行,如需首屏数据,请结合 useAsyncData 或 useFetch(…, { immediate: true })。

遵循以上模式,即可确保异步加载、状态更新与数据消费形成可靠的响应式链条,彻底规避 undefined 和竞态问题。

text=ZqhQzanResources