Vue中watchEffect触发无限循环原因及正确用法解析
问题原因分析
你的代码中watchEffect触发无限API调用的核心原因是**useAxios与watchEffect的响应式机制冲突**:
useAxios本身是封装了响应式状态(如内部的data、loadingref)的composable,它内部会自动处理请求发起与响应式依赖追踪。- 当你在
watchEffect的异步回调doGetRequest中调用useAxios时,watchEffect的同步执行阶段会间接关联到useAxios内部的响应式状态。当useAxios请求完成更新自身的dataref时,这个变化会被当前的watchEffect误判为依赖变化,从而触发watchEffect重新执行。 - 最终形成循环:
watchEffect执行 → 调用useAxios发起请求 → 请求完成更新useAxios内部状态 → 触发watchEffect再次执行 → 重复发起请求。
结合Vue官方文档的说明:> watchEffect only tracks dependencies during its synchronous execution,这里的问题在于useAxios的内部实现导致同步阶段就绑定了额外的响应式依赖,超出了你预期的仅追踪id的范围。
正确用法
有两种可靠的解决方案,可根据需求选择:
方案一:手动使用axios,配合watchEffect管理状态
放弃useAxios,改用原生axios发起请求,让watchEffect仅追踪你明确的依赖(即id),避免与useAxios的内部响应式机制冲突:
import axios from 'axios'; import { ComputedRef, isRef, ref, Ref, unref, watchEffect } from 'vue'; export function useGetRequest(id: string | Ref<string> | ComputedRef<string>) { const headers = { 'Content-Type': 'application/json', }; const data = ref<any>(); const loading = ref(false); const error = ref<Error>(); async function doGetRequest() { loading.value = true; error.value = undefined; try { const response = await axios.get(`https://dummyjson.com/products/${unref(id)}`, { headers }); data.value = response.data; } catch (err) { error.value = err as Error; } finally { loading.value = false; } } isRef(id) ? watchEffect(doGetRequest) : doGetRequest(); return { data, loading, error }; }
这里watchEffect的同步阶段仅访问unref(id),只会追踪id的变化;请求完成后修改data的操作属于异步阶段,不会被watchEffect追踪,因此不会触发无限循环。
方案二:利用useAxios自身的响应式能力
useAxios支持传入动态生成URL的函数,自动追踪函数内的响应式依赖,无需额外的watchEffect:
import { useAxios } from '@vueuse/integrations/useAxios'; import { ComputedRef, isRef, ref, Ref, unref } from 'vue'; export function useGetRequest(id: string | Ref<string> | ComputedRef<string>) { const headers = { 'Content-Type': 'application/json', }; // 将id统一转为响应式引用,确保useAxios能追踪其变化 const reactiveId = isRef(id) ? id : ref(id); // 传入返回URL的函数,useAxios会自动追踪reactiveId的变化并重新请求 const { data, loading, error } = useAxios<any>( () => `https://dummyjson.com/products/${unref(reactiveId)}`, { method: 'GET', headers } ); return { data, loading, error }; }
这种方案完全利用useAxios的内置能力,避免了手动使用watchEffect带来的冲突,代码更简洁。
内容的提问来源于stack exchange,提问作者charlycou
相关产品推荐
相关产品推荐

