Skip to content

watch 循环触发 ​

问题现象 ​

输入一次却发出多次请求,出现 Maximum recursive updates exceeded,或者两个筛选条件持续互相回写。

适用环境 ​

Vue 3;watch、computed 与表单联动。

本文是可复用的排障示例,不代表某次个人生产事故。命令中的名称、账号和数据均为示例,执行前请替换为已确认的目标。

复现或诊断步骤 ​

  1. 给每个 watcher 标注来源和新旧值,记录一次操作产生的调用链。
  2. 检查深度监听回调是否修改了被监听对象,尤其是排序数组、规范化字符串和补默认值。
  3. 暂停其中一个回写路径;若循环停止,说明形成了反馈环。日志避免输出表单中的敏感字段。

原因分析 ​

watch 的副作用修改了自己的依赖,或 A 修改 B、B 又修改 A。深度监听对象时,新旧值可能指向同一对象,不能仅靠引用比较判断字段是否真的变化。flush 配置改变执行时机,不能消除数据环。

修复示例 ​

ts
import { ref, computed, watch } from 'vue'
const keyword = ref('')
const normalized = computed(() => keyword.value.trim())
watch(normalized, (value, oldValue) => {
  if (value === oldValue) return
  // 将 value 交给查询函数,不回写 keyword
  console.debug('query changed', value.length)
})

把纯派生值交给 computed,把用户输入保留为唯一可写源。确需双向联动时,在业务入口明确修改顺序,并只在值不同时更新目标字段。请求还应配置防抖和过期结果丢弃,但防抖不是修复循环本身。

验证方法 ​

分别输入带空格文本、清空、程序回填和切换选项,统计规范化值每次变化对应的查询次数。组件卸载后应不再产生回调。

常见误区 ​

增加 setTimeout 只会把同步循环变成异步循环。deep: true 不应成为所有监听的默认设置;计算属性 getter 内也不要提交请求或修改状态。

官方参考链接 ​

相关分类:前端 Web · 全部笔记