TDesign的Menu菜单动态数据怎么保持展开状态?

W″树行 阅读 31

我在用TDesign做左侧导航栏时遇到了问题,菜单项是通过v-for动态生成的,但每次刷新页面后展开状态就会重置。我尝试用v-model:selected-keysdefault-expand-all属性都没搞定,代码大概是这样:


<template>
  <t-menu mode="inline" theme="light" :default-expanded=["/dashboard"]
    v-model:selected-keys="activeKeys">
    <t-sub-menu v-for="item in menus" :key="item.path" :value="item.path">
      <template #title>{{ item.title }}</template>
      <t-menu-item v-for="child in item.children" :key="child.path">
        {{ child.title }}
      </t-menu-item>
    </t-sub-menu>
  </t-menu>
</template>

现在的问题是:手动展开的菜单在数据更新后会收起,怎么才能让展开状态持久保持?查了文档也没找到合适的属性组合,有没有类似场景的解决经验?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
晓娜(打工版)
常见的解决方案是把展开状态也绑定到数据上,而不是只靠 default-expanded 这种静态属性。你现在的写法问题在于 default-expanded 只在初始化时生效一次,后续菜单重新渲染就会丢失。

你应该用 v-model:expanded 和一个响应式数组来维护展开的 key。比如:

<template>
<t-menu
mode="inline"
theme="light"
v-model:selected-keys="activeKeys"
v-model:expanded="expandedKeys">
<t-sub-menu
v-for="item in menus"
:key="item.path"
:value="item.path">
<template #title>{{ item.title }}</template>
<t-menu-item
v-for="child in item.children"
:key="child.path"
:value="child.path">
{{ child.title }}
</t-menu-item>
</t-sub-menu>
</t-menu>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const activeKeys = ref([])
const expandedKeys = ref([])

// 模拟从本地存储恢复状态
onMounted(() => {
const savedExpanded = localStorage.getItem('menuExpanded')
if (savedExpanded) {
expandedKeys.value = JSON.parse(savedExpanded)
}
})

// 监听展开状态变化并保存
watch(expandedKeys, (val) => {
localStorage.setItem('menuExpanded', JSON.stringify(val))
}, { deep: true })
</script>


关键点有两个:一是必须给 t-menu 加上 v-model:expanded 绑定,二是要把这个状态存到 localStorage 里,不然刷新页面还是会丢。TDesign 的 menu 组件不会自己持久化状态,得自己手动存取。

另外注意每个 submenu 和 menuitem 都要带 value 属性,不然 key 对不上也会失效。我之前踩过这坑,看着结构对了但就是不展开,最后发现是 value 没传。
点赞 4
2026-02-13 08:05
程序猿一然
你这个问题其实挺常见的,根本原因在于 TDesign 的 Menu 组件在动态数据重新渲染时,没法自动维持展开状态,光靠 default-expanded 是不行的,因为它只在初始化生效一次。

真正要解决得用 expanded-keys 这个受控属性,配合路由或者本地存储来持久化状态。我一般这样处理:

先把展开的 key 用一个 ref 存起来

const expandedKeys = ref([])


然后把组件上的 :default-expanded 换成 :expanded-keys@expand-change 来同步状态

<t-menu
mode="inline"
theme="light"
:expanded-keys="expandedKeys"
v-model:selected-keys="activeKeys"
@expand-change="(keys) => (expandedKeys.value = keys)"
>
<t-sub-menu v-for="item in menus" :key="item.path" :value="item.path">
<template #title>{{ item.title }}</template>
<t-menu-item v-for="child in item.children" :key="child.path">
{{ child.title }}
</t-menu-item>
</t-sub-menu>
</t-menu>


这样每次用户手动展开或收起,expandedKeys 都会更新。如果你想刷新后也保留,就再加一层 localStorage 同步

// 初始化时从 localStorage 恢复
const saved = localStorage.getItem('menuExpandedKeys')
expandedKeys.value = saved ? JSON.parse(saved) : ['/dashboard']

// 监听变化存回去
watch(expandedKeys, (val) => {
localStorage.setItem('menuExpandedKeys', JSON.stringify(val))
}, { deep: true })


这样更清晰,而且完全可控。TDesign 的文档确实没把这个讲清楚,但只要把 expanded-keys 变成受控模式,问题就解开了。
点赞 4
2026-02-08 22:24