Nuxt3中多次调用useFetch触发实例访问错误求助
解决Nuxt中多个useFetch调用触发实例访问错误的问题
错误原因
你遇到的报错是因为Nuxt的useFetch这类composable必须在Vue setup函数的同步执行阶段、Nuxt插件/钩子/中间件中调用。而你将useFetch放在嵌套的异步函数里,通过await链式调用init(),导致第二个useFetch执行时,已经脱离了setup的同步上下文,无法访问Nuxt实例。
解决方案
方案1:顶层await + Promise.all并行请求(推荐)
直接在<script setup>顶层并行发起两个API请求,避免嵌套异步导致的上下文丢失,同时提升请求效率:
<script setup> // 通用状态 const alreadyCheckedIn = ref(false); const showModal = ref(false); const lecture = ref(null); // 弹窗状态 const lectureName = ref(""); const startTime = ref(""); // 错误处理 const displayError = ref(false); const errorMessage = ref("Please fill in all fields."); const lectureDateStr = new Date().toISOString().split("T")[0]; // 并行发起两个请求 const [lectureRes, checkInRes] = await Promise.all([ useFetch("/api/lecture", { method: "GET", query: { lectureDate: lectureDateStr, groupId: 1, // TODO: 从用户信息获取groupId }, }), useFetch("/api/check-in", { method: "GET", query: { lectureDate: lectureDateStr, userId: 1, // TODO: 从用户信息获取userId groupId: 1, // TODO: 从用户信息获取groupId }, }) ]); // 处理课程数据 if (lectureRes.data.value && lectureRes.data.value.statusCode !== 404) { lecture.value = JSON.parse(lectureRes.data.value.body); lecture.value.start_time = new Date(lecture.value.start_time) .toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" }); } // 处理签到状态 alreadyCheckedIn.value = checkInRes.data.value && checkInRes.data.value.statusCode !== 404; </script>
方案2:保留函数封装,提前调用useFetch
如果需要保留原有函数封装的结构,可以在setup顶层先调用useFetch,再将数据处理逻辑封装到函数中:
<script setup> // 通用状态 const alreadyCheckedIn = ref(false); const showModal = ref(false); const lecture = ref(null); // 弹窗状态 const lectureName = ref(""); const startTime = ref(""); // 错误处理 const displayError = ref(false); const errorMessage = ref("Please fill in all fields."); const lectureDateStr = new Date().toISOString().split("T")[0]; // 在setup顶层调用useFetch,确保上下文有效 const { data: lectureData } = useFetch("/api/lecture", { method: "GET", query: { lectureDate: lectureDateStr, groupId: 1, // TODO: 从用户信息获取groupId }, }); const { data: checkInData } = useFetch("/api/check-in", { method: "GET", query: { lectureDate: lectureDateStr, userId: 1, // TODO: 从用户信息获取userId groupId: 1, // TODO: 从用户信息获取groupId }, }); // 封装课程数据处理逻辑 function processLectureData() { if (!lectureData.value || lectureData.value.statusCode === 404) return; lecture.value = JSON.parse(lectureData.value.body); lecture.value.start_time = new Date(lecture.value.start_time) .toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" }); } // 封装签到状态处理逻辑 function processCheckInStatus() { alreadyCheckedIn.value = checkInData.value && checkInData.value.statusCode !== 404; } // 监听数据变化,立即执行处理逻辑 watch(lectureData, processLectureData, { immediate: true }); watch(checkInData, processCheckInStatus, { immediate: true }); </script>
方案3:用onMounted包裹初始化函数
如果必须保留原有init函数的结构,可将init的调用放在onMounted钩子中,确保composable在Vue生命周期上下文内执行:
<script setup> import { onMounted } from 'vue'; // 通用状态 const alreadyCheckedIn = ref(false); const showModal = ref(false); const lecture = ref(null); // 弹窗状态 const lectureName = ref(""); const startTime = ref(""); // 错误处理 const displayError = ref(false); const errorMessage = ref("Please fill in all fields."); // 获取课程数据 async function fetchLectureData() { const lectureDateStr = new Date().toISOString().split("T")[0]; const { data } = await useFetch("/api/lecture", { method: "GET", query: { lectureDate: lectureDateStr, groupId: 1, // TODO: 从用户信息获取groupId }, }); if (data.value.statusCode === 404) { return; } return data.value.body ? await JSON.parse(data.value.body) : null; } // 检查签到状态 async function checkIfCheckedIn() { const lectureDateStr = new Date().toISOString().split("T")[0]; const { data } = await useFetch("/api/check-in", { method: "GET", query: { lectureDate: lectureDateStr, userId: 1, // TODO: 从用户信息获取userId groupId: 1, // TODO: 从用户信息获取groupId }, }); if (data.value.statusCode === 404) { return false; } return true; } // 初始化逻辑 async function init() { const lectureFromDB = await fetchLectureData(); if (lectureFromDB) { lecture.value = lectureFromDB; lecture.value.start_time = new Date(lecture.value.start_time) .toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" }); } alreadyCheckedIn.value = await checkIfCheckedIn(); } // 在组件挂载后执行初始化 onMounted(init); </script>
内容的提问来源于stack exchange,提问作者Julius
相关产品推荐
相关产品推荐

