You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue递归子组件更新/移除的优化方案咨询

Vue递归子组件更新/移除的优化方案咨询

我最近在做一个Vue应用,需要实现类似文件系统的目录结构遍历和展示功能,先给大家看看我目前的实现代码和遇到的问题。

首先是根组件App.vue:

<script setup>
import ListItem from './components/ListItem.vue';

const data = {
  'C:/': {
    'users/': {
      'testUser/': {
        'documents/': {},
      },
    },
    'windows/': {
      'system32': {},
    },
  },
  'E:/': {
    'test/': {},
  },
};
</script>

<template>
  <div style="display: flex; flex-direction: column">
    <h1>Recursive Directory Browser</h1>
    <ListItem title="Browse" :items="data" />
  </div>
</template>

<style scoped></style>

然后是递归使用的ListItem.vue组件:

<script setup>
import { defineProps, ref, nextTick } from 'vue';

const props = defineProps({
  title: {
    type: String,
    required: true,
  },
  items: {
    type: Object,
    required: true,
  },
});

const selectedName = ref();
const selectedValue = ref();

async function updateSelected(key, value) {
  selectedName.value = key;
  selectedValue.value = value;
}
</script>

<template>
  <div style="border: 2px solid black; margin: 1em; padding: .25em;">
    <h2>{{ title }}</h2>
    <div style="display: flex; flex-direction: row;">
      <template v-for="(value, key) in items">
        <button @click="updateSelected(key, value)">{{ key }}</button>
      </template>
    </div>
  </div>
  <template v-if="selectedName">
      <ListItem
        :title="selectedName"
        :items="selectedValue"
      />
    </template>

</template>

<style scoped></style>

这个实现能正常生成递归的目录浏览器,但有个头疼的bug:当我深入几层目录后,回到父目录选择另一个节点,之前展开的那些子目录组件不会自动消失,就留在界面上了。

我自己临时想了个解决办法,给ListItem加了个updating属性,结合nextTick()来强制刷新,修改后的ListItem.vue代码是这样的:

<script setup>
import { defineProps, ref, nextTick } from 'vue';

const props = defineProps({
  title: {
    type: String,
    required: true,
  },
  items: {
    type: Object,
    required: true,
  },
  updating: {
    type: Boolean,
    required: false,
    default: false,
  },
});

const updating = ref(props.updating);
const selectedName = ref();
const selectedValue = ref();

async function updateSelected(key, value) {
  updating.value = true;
  await nextTick();
  selectedName.value = key;
  selectedValue.value = value;
  updating.value = false;
}
</script>

<template>
  <div style="border: 2px solid black; margin: 1em; padding: .25em;">
    <h2>{{ title }}</h2>
    <div style="display: flex; flex-direction: row;">
      <template v-for="(value, key) in items">
        <button @click="updateSelected(key, value)">{{ key }}</button>
      </template>
    </div>
  </div>
  <template v-if="selectedName && !updating">
      <ListItem
        :title="selectedName"
        :items="selectedValue"
        :updating="updating"
      />
    </template>

</template>

<style scoped></style>

虽然这个办法能解决问题,但总觉得有点绕,依赖nextTick()和额外加的属性总不是最优解。想问问大家有没有更优雅的方案,不需要用这些临时手段就能解决这个子组件残留的问题?

备注:内容来源于stack exchange,提问作者Ben Foltz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 16:58:00