Vue 3 Composition API ref原始属性响应式失效问题排查
问题根因
代码存在多个核心问题直接导致响应式失效:
selectedTitle是独立ref,初始化时仅取了tabTitles首次计算的第一个值做静态赋值,和tabTitles没有任何响应式绑定,后续tabTitles更新不会自动同步到该ref。- watcher监听源写法错误:
() => tabTitlesLength返回的是computed ref对象本身,没有取.value,watcher根本无法追踪到长度变化。 - watcher配置冗余错误:给基础类型值的监听加了无意义的
deep: true,触发了引用类型监听时新旧值同引用的已知问题,导致新旧值判断逻辑完全失效。 - 回调赋值逻辑错误:
selectedTitle本身已经是ref实例,给selectedTitle.value赋值时额外包裹了一层ref(),实际存入的是ref实例而非标签标题字符串。 - 原有判断逻辑有缺陷:仅通过长度变小触发更新,没有校验当前选中标签是否已被移除,无法覆盖所有选中值失效的场景。
修复方案
按以下步骤调整即可实现预期效果:
- 移除冗余的
tabTitlesLength计算属性,减少无效代码。 - 修正watcher监听源为
() => tabTitles.value,移除不必要的deep: true配置,只要标签列表发生变化就触发校验。 - 调整校验逻辑:不依赖新旧长度对比,直接判断当前
selectedTitle.value是否存在于最新的标签列表中,不存在则默认选中列表第一个标签,覆盖所有选中值失效的场景。 - 修正赋值逻辑,给
selectedTitle.value赋值时直接传入标题字符串,去掉多余的ref()包裹。
修正后的核心script代码如下:
<script> import { ref, computed, useSlots, provide, watch } from "vue"; export default { props: { defaultTagMenu: { type: Boolean, default: true, }, }, setup() { const slots = useSlots(); const tabTitles = computed(() => slots.default()[0].children.map((tab) => tab.props.title) ); const selectedTitle = ref(tabTitles.value[0]); provide("selectedTitle", selectedTitle); provide("tabTitles", tabTitles); watch( () => tabTitles.value, (newTitles) => { // 当前选中标签不存在于列表时,默认选中第一个标签 if (!newTitles.includes(selectedTitle.value)) { selectedTitle.value = newTitles[0]; } } ); return { tabTitles, selectedTitle, }; }, }; </script>
补充优化:如果需要更符合用户操作习惯的交互,比如关闭当前选中标签时自动选中相邻标签而非回到第一个,可以在校验逻辑中先获取当前选中项的索引,优先选中前一个/后一个标签即可,核心响应式逻辑不变。
内容的提问来源于stack exchange,提问作者Lowtrux
相关产品推荐
相关产品推荐

