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

Vue2中如何动态构建无限层级嵌套菜单组件树?

构建无限层级嵌套菜单的最优方案

你当前用v-html的方式行不通——Vue不会编译v-html渲染的内容,里面的@click="sayHello()"根本不会绑定到组件的方法上,点击完全没反应。

构建嵌套菜单的最优方案是用递归组件:Vue允许组件自身调用自身,完美适配树形结构的嵌套需求。

具体实现步骤

1. 定义菜单数据结构

先准备树形结构的菜单数据,每个节点包含显示文本、子菜单(可选)、点击回调(可选):

const menuData = [
  {
    label: "一级菜单1",
    click: () => console.log("点击一级菜单1"),
    children: [
      {
        label: "二级菜单1-1",
        children: [
          { label: "三级菜单1-1-1", click: () => console.log("点击三级菜单1-1-1") }
        ]
      },
      { label: "二级菜单1-2", click: () => console.log("点击二级菜单1-2") }
    ]
  },
  { label: "一级菜单2", click: () => console.log("点击一级菜单2") }
];

2. 编写递归组件

给组件设置name选项(必须,否则无法递归调用),模板里渲染当前菜单项,同时判断是否有子菜单,有就递归渲染:

<template>
  <ul class="menu-list">
    <li v-for="(item, index) in menuItems" :key="index" class="menu-item">
      <div class="menu-label" @click="handleClick(item)">
        {{ item.label }}
        <!-- 有子菜单时显示展开/折叠图标 -->
        <span v-if="item.children" class="arrow">▼</span>
      </div>
      <!-- 递归渲染子菜单,通过展开状态控制显示 -->
      <menu-item 
        v-if="item.children && item.expanded" 
        :menu-items="item.children"
      />
    </li>
  </ul>
</template>

<script>
export default {
  name: "MenuItem", // 必须命名,才能递归调用自身
  props: {
    menuItems: {
      type: Array,
      required: true
    }
  },
  methods: {
    handleClick(item) {
      // 执行节点自带的点击回调
      if (item.click) item.click();
      // 有子菜单则切换展开状态
      if (item.children) {
        item.expanded = !item.expanded;
      }
    }
  }
};
</script>

<style scoped>
.menu-list {
  list-style: none;
  padding-left: 20px;
  margin: 0;
}
.menu-item {
  margin: 8px 0;
}
.menu-label {
  cursor: pointer;
  padding: 4px;
}
.arrow {
  margin-left: 8px;
  font-size: 12px;
}
</style>

3. 在父组件中使用

把根菜单数据传给递归组件即可:

<template>
  <div class="menu-container">
    <menu-item :menu-items="menuData" />
  </div>
</template>

<script>
import MenuItem from "./MenuItem.vue";

export default {
  components: { MenuItem },
  data() {
    return {
      menuData: [
        // 放入之前定义的菜单数据
      ]
    };
  }
};
</script>

补充说明

  • 展开/折叠逻辑:直接把expanded状态存在菜单数据里,简单直观,也可以改用组件内部的响应式状态管理。
  • Vue 3 Composition API写法:逻辑类似,只需把props和方法放到setup里,注意要单独加<script>块给组件命名:
<template>
  <!-- 模板和之前一致 -->
</template>

<script>
export default {
  name: "MenuItem"
};
</script>

<script setup>
import { defineProps } from 'vue';
const props = defineProps({
  menuItems: {
    type: Array,
    required: true
  }
});

const handleClick = (item) => {
  if (item.click) item.click();
  if (item.children) {
    item.expanded = !item.expanded;
  }
};
</script>

内容的提问来源于stack exchange,提问作者TheMan68

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 23:10:32