当前位置: 首页 > news >正文

Vue.js报错-Maximum-recursive-updates-exceeded

Vue.js 报错:Maximum recursive updates exceeded in component

一句话总结:用「watch + computed + nextTick」三件套,让响应式更新不再无限递归,警告瞬间消失!


正文目录

  1. 报错含义:Vue 在警告什么「递归更新」?
  2. 5 大高频翻车场景 & 修复代码
  3. 万能兜底:watch、computed、nextTick
  4. 预防 checklist(不再踩坑)
  5. 一句话总结

一、报错含义:Vue 在警告什么「递归更新」?

当你在控制台看到:

Maximum recursive updates exceeded in component <xxx>. This means you have a reactive effect that is mutating its own dependency and recursively triggering itself.

Vue 在告诉你:

「你的响应式逻辑形成了无限循环 —— A 修改了 B,B 变化又触发修改 A,A 又修改 B……死循环了!」

Vue 内置了递归保护机制,当同一个组件的渲染函数被递归触发超过 100 次时,就会抛出此警告并停止更新。

本质:响应式依赖形成了循环引用


二、5 大高频翻车场景 & 修复代码

① watch 修改了被监听的数据 —— 自我递归
<script setup> import { ref, watch } from 'vue'; const count = ref(0); // ❌ watch 修改了被监听的 count,count 变化又触发 watch watch(count, (newVal) => { count.value = newVal + 1; // ❌ 死循环 }); </script>

修复:用中间变量或条件退出

<script setup> import { ref, watch } from 'vue'; const count = ref(0); // ✅ 加退出条件,避免无限递归 watch(count, (newVal) => { if (newVal >= 10) return; // ✅ 有上限 count.value = newVal + 1; }); </script>
② computed 里修改了依赖的响应式数据
<script setup> import { ref, computed } from 'vue'; const price = ref(100); const count = ref(2); // ❌ computed 中修改了依赖的 price const total = computed(() => { price.value = price.value + 10; // ❌ 修改依赖 → 死循环 return price.value * count.value; }); </script>

修复:computed 只读,不修改依赖

<script setup> import { ref, computed } from 'vue'; const price = ref(100); const count = ref(2); // ✅ computed 只做计算,不修改依赖 const total = computed(() => price.value * count.value); // ✅ 需要修改 price,用 watch 或事件 function updatePrice() { price.value += 10; } </script>
③ 模板中绑定了互相依赖的数据
<script setup> import { ref, computed } from 'vue'; const firstName = ref('张'); const lastName = ref('三'); // ❌ fullName 依赖 firstName,firstName 又依赖 fullName const fullName = computed(() => { firstName.value = fullName.value; // ❌ 互相依赖 return firstName.value + lastName.value; }); </script>

修复:单向数据流,避免互相依赖

<script setup> import { ref, computed } from 'vue'; const firstName = ref('张'); const lastName = ref('三'); // ✅ 单向依赖:fullName 依赖 firstName,但 firstName 不依赖 fullName const fullName = computed(() => firstName.value + lastName.value); // ✅ 需要反向更新,用 watch watch(fullName, (val) => { // 单独处理 }); </script>
④ watch 监听 computed,又在回调中修改 computed 的源数据
<script setup> import { ref, computed, watch } from 'vue'; const list = ref([1, 2, 3]); // ❌ doubleList 依赖 list,watch 修改 list → doubleList 变化 → watch 触发 const doubleList = computed(() => list.value.map(i => i * 2)); watch(doubleList, () => { list.value.push(list.value.length + 1); // ❌ 修改源数据 → 死循环 }); </script>

修复:监听不同数据源,或用 once 标志

<script setup> import { ref, computed, watch } from 'vue'; const list = ref([1, 2, 3]); const doubleList = computed(() => list.value.map(i => i * 2)); // ✅ 监听 doubleList 的长度而非内容,或加条件 watch(() => doubleList.value.length, (newLen, oldLen) => { if (newLen === oldLen) return; // ✅ 避免无意义触发 // 只在真正需要时更新 }); </script>
⑤ 组件 props 和 emit 形成循环
<!-- 父组件 --> <script setup> import { ref } from 'vue'; import Child from './Child.vue'; const value = ref(''); const handleUpdate = (val) => { value.value = val.toUpperCase(); // ❌ 转换后又传给子组件 → 子组件 emit → 再传回 }; </script> <template> <Child :modelValue="value" @update:modelValue="handleUpdate" /> </template>
<!-- 子组件 --> <script setup> const props = defineProps(['modelValue']); const emit = defineEmits(['update:modelValue']); // ❌ watch props 变化后立即 emit,父组件又修改 → 循环 watch(() => props.modelValue, (val) => { emit('update:modelValue', val.toLowerCase()); // ❌ 死循环 }); </script>

修复:子组件不做反向转换,或用 computed 只读

<!-- 子组件(修复版) --> <script setup> import { computed } from 'vue'; const props = defineProps(['modelValue']); const emit = defineEmits(['update:modelValue']); // ✅ 用 computed 展示转换后的值,不 emit 回去 const displayValue = computed(() => props.modelValue.toLowerCase()); // ✅ 只在用户输入时 emit function handleInput(e) { emit('update:modelValue', e.target.value); } </script>

三、万能兜底:watch、computed、nextTick

场景工具示例
需要修改被监听的数据watch + 条件退出if (val >= 10) return;
需要派生数据computed(只读)computed(() => a + b)
需要延迟更新nextTicknextTick(() => { ... })
需要批量更新watch + flush:‘post’watch(src, fn, { flush: 'post' })

万能模板

<script setup> import { ref, watch, nextTick } from 'vue'; const data = ref(null); // ✅ 万能模板:条件退出 + nextTick 延迟 watch(data, async (newVal) => { if (!newVal) return; // ✅ 条件退出 await nextTick(); // ✅ 延迟到 DOM 更新后 // 安全地修改数据 }); </script>

四、预防 checklist

  • watch 回调绝不直接修改被监听的源数据
  • computed只做计算,不产生副作用
  • 响应式数据之间避免互相依赖(A 依赖 B,B 又依赖 A)
  • watch 监听 computed 时,不在回调中修改 computed 的源数据
  • 父子组件 props/emit不做双向转换
  • 控制台「Maximum recursive updates」= 立即检查响应式依赖链是否有环

五、一句话总结

「Maximum recursive updates exceeded」= 响应式依赖形成了循环引用。

用「watch + 条件退出 + computed 只读」三件套,让响应式更新不再无限递归,警告瞬间消失!


最后问候亲爱的朋友们,并邀请你们阅读我的全新著作

📚 《Vue.js 3企业级项目开发实战(微课视频版》

http://www.cnnetsun.cn/news/3598147.html

相关文章:

  • 自学黑客(网络安全入门)
  • 爱车开销:你的智能养车好帮手
  • 实时排名系统技术解析:Redis有序集合与暗票机制实现
  • 十、Redis之布隆过滤器
  • 齐悟同源微囊体究竟是啥东西
  • TI Stellaris LM4F232评估板:从Cortex-M4F到USB OTG与CAN总线的嵌入式开发实战
  • TMS470 ARM7开发套件快速入门:从环境搭建到LED闪烁实战
  • C++编译时混淆技术:基于Clang插件保护核心代码
  • 为什么92%的AI客户管理项目半年内失效?揭秘头部企业私有化部署的3个核心风控节点
  • 本地部署AI项目183.0:环境配置、性能优化与批量任务实践
  • 从学习效率翻倍到开源机器人栈:AI智能体正在长出身体,但人形交互仍是最后一道关卡
  • FFmpeg音视频分离实战:从原理到批量移除音频流
  • 从半导体到红外:气体传感器家族大盘点
  • 影刀RPA 网页表单填写的避坑大全
  • 为什么不同平台检测同一篇论文AI率差距大深度解读:AIGC检测平台算法差异完整分析
  • 角色拉远切低模后,渲染线程为何还是很高
  • SERP抓取做排名追踪:requests加BeautifulSoup够不够用
  • 基于XR Interaction Toolkit的PICO 4沉浸式交互开发实战
  • 财务因子在公告前就出现:国内量化软件如何阻断未来数据
  • 干货:如何利用AAV精准靶向肝巨噬细胞
  • DeepSeek LeetCode 3686. 稳定子序列的数量 C++实现
  • 视频太长看不完?用AI自动转写逐字稿+要点总结+思维导图,5分钟看完2小时视频
  • 多平台电商订单集成的技术架构选型:如何评估聚合接口的稳定性与数据安全
  • Unity Asset Bundle资源提取方案:解析引擎、依赖图谱与格式转换
  • Git 与 GitHub 核心协作流:历史重写机制与标准 PR 实践
  • Unity AR Foundation实战指南:从零构建Spatial Joy 2025跨平台AR应用
  • AI实验室:科研工具链的智能化变革
  • C++异常处理性能深度剖析:从原理到实践的性能优化指南
  • 深入解析TI DCAN模块:中断、电源与错误处理三大核心机制
  • C/C++自动化交互编程:Expect库核心原理与实战应用