Vue中reactive()导致checkRule函数递归执行的原因分析
Vue中reactive数组导致函数递归执行的原因?
问题描述
我的Vue应用里有个bug:在checkRule函数中,只执行绿色代码(注释掉errors.push("Testerror");及对应日志)时,console.log会准确执行两次(因有2个必填列存在空值),符合预期;但同时执行绿色与红色代码时,两个console.log会出现递归执行的情况。移除变量errors的reactive()修饰后,运行恢复正常。请问该现象是否与reactive()有关?原因是什么?
代码示例
<script setup> import { reactive } from 'vue' let errors = reactive([]); const product_columns = [ { name: "name", mandatory: true }, { name: "brand", mandatory: false } ]; const products = [ { id: 1, name: "Product1", brand: "TestBrand" }, { id: 2, name: "", brand: "" }, { id: 3, name: "Product3", brand: "TestBrand" }, { id: 4, name: "", brand: "TestBrand" }, { id: 5, name: "Product5", brand: "TestBrand" }, { id: 6, name: "Product6", brand: "" }, { id: 7, name: "Product7", brand: "TestBrand" }, ]; function checkRule(name, value, index) { if (value == "") { // 如果是必填列 if(product_columns.find(product => product.name === name).mandatory) { // 本应只在索引1和3的产品上打印(因为这两处name为空) console.log("Column " + name + " is mandatory on index: " + (index)); errors.push("Testerror"); console.log(errors); // 返回错误样式类 return "bg-red-100" } } } </script> <template> <div v-for="(product, index) in products" :key="product.id"> <div :class="checkRule('name', product.name, index)"> {{ product.name }} </div> <div :class="checkRule('brand', product.brand, index)"> {{ product.brand }} </div> <br> </div> </template>
问题解答
结论
这个现象完全和reactive()有关,核心原因是Vue的响应式更新机制触发了重复渲染,进而导致checkRule函数被反复调用。
具体原因
- 响应式数组变更触发组件重渲染:当你调用
errors.push("Testerror")时,errors是reactive创建的响应式数组,这个修改会被Vue检测到,立刻触发组件重新渲染。 - 模板中的函数会在每次渲染时重新执行:模板里用
:class="checkRule(...)"这种方式绑定函数,意味着组件每渲染一次,所有循环项的checkRule都会被重新调用一遍。 - 形成无限渲染闭环:第一次渲染时
checkRule执行,发现空值后修改errors触发重渲染;重渲染时所有checkRule再次执行,又会修改errors再次触发渲染,这个循环不断重复,就出现了日志递归打印的情况。 - 移除
reactive()恢复正常的原因:如果errors不是响应式的,errors.push不会触发Vue的响应式更新,组件只会初始渲染一次,checkRule也只会执行一次,自然不会出现重复调用的问题。
修复建议
- 避免在模板属性绑定中直接调用会修改响应式状态的函数,这极易引发无限渲染。
- 提前计算错误状态:比如在组件挂载时(
onMounted)或者数据变化时(用watch监听products)批量校验,把结果存在变量中,模板直接使用预计算好的结果。 - 调整校验时机:比如在提交表单、用户输入完成后再执行校验,而不是在组件渲染过程中做这件事。
内容的提问来源于stack exchange,提问作者MaVaBo
相关产品推荐
相关产品推荐

