Vue.js响应式变量与变更追踪异常问题排查
解决Vue中点击消息列表重复选中、首次点击无响应的问题
问题现象
点击消息列表时,首次点击没有选中效果;第二次点击会出现两条重复的选中消息,点击次数越多,重复的内容越多。
问题根源
- 重复绑定监听:每次点击消息都调用
SelectedItem函数,而这个函数每次执行都会给selectedMessage新增一个watch监听。点击次数越多,监听函数数量就越多,导致后续每次点击触发多次回调,出现重复选中。 - 循环触发watch:
SelectedItem里的chooseItem函数又给selectedValue.value赋值,这会再次触发watch回调,形成无效循环,干扰正常选中逻辑。 - 错误的状态覆盖:下拉组件里用
SelectedItem的返回值直接覆盖selectedMessage.value,但返回的是初始空值,会冲掉后续选中状态。
修复方案
1. 重构SelectedItem工具函数
修改成只绑定一次监听,移除循环赋值的逻辑:
import { watch, Ref } from "vue"; export default function SelectedItem<Type>(selectedValue: Ref<Type>, callback?: (item: Type) => void) { // 仅初始化一次watch监听 watch(selectedValue, (newVal) => { if (newVal && callback) { callback(newVal); console.log(newVal); } }); }
2. 调整消息列表组件的调用逻辑
初始化时只调用一次SelectedItem,点击时直接更新ref值:
<template> <div> <ul> <li v-for="(message, index) in SMSMessages" :key="index" @click="selectMessage(message)" > <p>Body: {{ message.body }}</p> <p>Origin Number: {{ message.originNumber }}</p> <p>Target Number: {{ message.targetNumber }}</p> <p>Direction: {{ message.direction }}</p> <p>Message Date: {{ message.messageDt }}</p> </li> </ul> </div> </template> <script setup lang="ts"> import SMSMessages from "../data/sms-messages.json"; import MessageInterface from "../Interfaces/MessageInterface"; import { ref } from "vue"; import SelectedItem from "../Interfaces/SelectedItem"; const selectedMessage = ref<MessageInterface | null>(null); // 初始化时绑定监听,后续无需重复调用 SelectedItem(selectedMessage, (selected) => { // 这里可以写选中后的处理逻辑,比如同步状态到其他组件 }); const selectMessage = (message: MessageInterface) => { // 直接更新响应式状态,watch会自动触发回调 selectedMessage.value = message; }; </script>
3. 修复下拉选择组件代码
移除错误的状态覆盖,只初始化监听:
<template> <div> <select v-model="selectedMessage"> <option value="">Vyberte cílové číslo</option> <option v-for="(message, index) in SMSMessages" :key="index" :value="message.targetNumber" > {{ message.targetNumber }} </option> </select> <p>Vybráno: {{ selectedMessage }}</p> </div> </template> <script setup lang="ts"> import SMSMessages from "../data/sms-messages.json"; import { ref } from "vue"; import SelectedItem from '../Interfaces/SelectedItem' const selectedMessage = ref<string>(""); // 仅初始化监听即可 SelectedItem(selectedMessage, (number) => { console.log("选中的目标号码:", number); }); </script>
修复后逻辑说明
SelectedItem现在只负责绑定一次监听,通过回调函数传递选中值,避免重复绑定和循环触发问题。- 所有组件在初始化时调用一次
SelectedItem,后续操作直接更新响应式状态,watch自动处理后续逻辑。 - 去掉了无效的状态覆盖,保证响应式数据正常更新。
内容的提问来源于stack exchange,提问作者Peter_Griffindor
相关产品推荐
相关产品推荐

