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

Vue中watchEffect触发无限循环原因及正确用法解析

问题原因分析

你的代码中watchEffect触发无限API调用的核心原因是**useAxios与watchEffect的响应式机制冲突**:

  • useAxios本身是封装了响应式状态(如内部的data、loading ref)的composable,它内部会自动处理请求发起与响应式依赖追踪。
  • 当你在watchEffect的异步回调doGetRequest中调用useAxios时,watchEffect的同步执行阶段会间接关联到useAxios内部的响应式状态。当useAxios请求完成更新自身的data ref时,这个变化会被当前的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:17:28