Vue v-if条件未满足时内部子组件仍提前接收数据问题排查
问题现象
在v-if绑定条件未满足的预期下,嵌套的PkSelect子组件提前触发渲染、接收到了传入的props值,相关代码与日志如下:
模板代码
<div v-if="mappingData && mappingData.length && !loading"> <div v-for="element in mappingData" :key="element.attribute" > <PkSelect :preselectIndex="[element.value]" :options="element.select_options" v-model:optionIndexModel="element.value"></PkSelect> </div> </div>
递归赋值逻辑
const recursiveFuzzyFill = () => { let awaitme = new Promise((resolve, reject) => { try { if (mappingData.value[loopIndex] && fuse.search(mappingData.value[loopIndex].label).length) { var refIndex = fuse.search(mappingData.value[loopIndex].label)[0].refIndex; mappingData.value[loopIndex].value = refIndex; console.log("Value:", mappingData.value[loopIndex].value); resolve(); } else { resolve(); } } catch (error) { reject(); loopIndex = 0; return; } }); awaitme.then(() => { if (loopIndex === (mappingData.value.length - 1)) { setTimeout(() => { loopIndex = 0; loading.value = false; console.log("Loading:", loading.value); }, 5000); return; } else { loopIndex++; recursiveFuzzyFill(); } }); }
实际运行日志
Value: 16 PkSelect.vue:709 Preselect Index: 16 PkSelect.vue:709 Preselect Index: null PkSelect.vue:709 Preselect Index: null PkSelect.vue:709 Preselect Index: null PkSelect.vue:709 Preselect Index: null PkSelect.vue:709 Preselect Index: null PkSelect.vue:709 Preselect Index: null ConfigMapping.vue:184 Value: 27 ConfigMapping.vue:184 Value: 13 ConfigMapping.vue:184 Value: 18 ConfigMapping.vue:201 Loading: false
预期逻辑是loading在所有数据处理完成5秒后置为false,此时v-if条件才满足、子组件才渲染,但实际子组件在日志输出第一行Value:16时就已经开始接收参数。
问题产生原因
这个现象是三个执行时序问题叠加造成的:
- loading状态控制逻辑本身有漏洞:翻遍
recursiveFuzzyFill的代码,只有最后5秒延时回调里设置了loading.value = false,没有任何代码在数据处理开始时将loading设为true。如果loading初始值就是false,那只要mappingData被赋值为非空数组,v-if的三个判断条件直接全部满足,根本不会等你那5秒延时。如果你本来设了loading初始值为true,那大概率是给mappingData赋值的时机早于你把loading设为true的时机,首次响应式更新时v-if条件已经短暂成立,直接触发了子组件渲染。 - Vue的DOM更新本身是异步批量执行的:所有响应式状态的修改不会立刻触发DOM重渲染,Vue会把更新任务推到微任务队列,等当前同步代码执行完后统一处理。你写的递归逻辑里,
new Promise传入的executor是完全同步运行的,then回调也属于微任务,和Vue的DOM更新任务在同一个队列里。当你第一次给mappingData第一项赋值value=16时就触发了响应式,组件更新任务立刻入队,这时候你后续的递归逻辑(给其他项赋值、最后设置loading状态)还排在微任务队列更靠后的位置,DOM更新先执行,直接渲染出了只完成第一项赋值的PkSelect组件,打出第一条Preselect Index:16和剩下6条null的日志。 - 无意义的Promise封装打乱了执行顺序:你的fuse搜索逻辑全是同步代码,根本不需要用Promise包裹。现在的写法把递归逻辑放在
then回调里,人为拉长了微任务执行链,直接导致Vue的DOM更新任务插在了第一次赋值和后续递归赋值的中间,才出现组件提前渲染、只拿到部分数据的情况。
从日志就能验证这个判断:7条PkSelect的preselect日志全部出现在后面3条Value日志之前,说明组件在你处理完第一个元素的时候就已经完成首次挂载了,后面给其他元素赋值属于组件挂载后的props更新,如果你的preselectIndex相关逻辑只在组件mounted阶段执行,自然不会再打印新的preselect日志。
内容的提问来源于stack exchange,提问作者temp
相关产品推荐
相关产品推荐

