最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue3和Element Plus的遞歸組件實現(xiàn)多級導航欄功能(示例代碼)

 更新時間:2025年06月17日 11:15:48   作者:ArabySide  
本文基于 Vue3 和 Element Plus,通過遞歸組件實現(xiàn)了可動態(tài)渲染的多級導航欄,利用自引用數(shù)據(jù)結構和深度控制避免無限循環(huán),同時結合 TypeScript 規(guī)范數(shù)據(jù)類型并優(yōu)化組件封裝,感興趣的朋友一起看看吧

前言

在日常工作中,我能經(jīng)常能碰到那種自引用類似的數(shù)據(jù)結構。比方說樹形的導航欄結構,我們期望Vue渲染時遞歸遍歷組件樹,直到葉子節(jié)點。一個組件在其自身的模板中調用自身的組件,這便是Vue中的遞歸組件。

一、遞歸的意義

想象一個這樣的一個對象數(shù)組。每個對象有基本的path路徑,name名稱,label標簽,icon圖標。這四個屬性是必須的,個別對象本身有children屬性包含它的子菜單內(nèi)容。并且children屬性本身也是對應一個包含path,name,label,icon四個基本屬性和children可選屬性的對象。

這樣的話我們就構建了一個典型的自引用類似的數(shù)據(jù)結構,相互之間互相嵌套。相信大家已經(jīng)發(fā)現(xiàn)了,在我們不知道嵌套深度的情況下,是沒法通過循環(huán)完整解析出全部數(shù)據(jù)。并且遞歸的邏輯復用性是優(yōu)于循環(huán)。這樣的結果也會使遞歸在代碼居然簡潔性,靈活性,方便擴展性。

二、遞歸組件的實現(xiàn)——基于element-plus UI的多級導航欄

2.1 element-plus Menu菜單官方示例

我們接下來要開發(fā)的多級導航欄是基于element-plus的menu菜單組件。觀察官方代碼結構,組件最由el-menu標簽包裹,el-sub-menu為一級菜單,內(nèi)嵌的el-menu-item為一級菜單的子內(nèi)容。但el-sub-menu里面也可以繼續(xù)添加el-sub-menu標簽,作為一個二級菜單。實現(xiàn)組件樹的結構。

樹狀導航欄代碼如下:

<el-menu>                 <!-- 最外層菜單容器 -->
    ├─ <el-sub-menu>         <!-- 一級菜單(可展開) -->
    │   ├─ 菜單標題(含圖標和文本)
    │   └─ <el-sub-menu>       <!-- 二級菜單(嵌套在一級菜單內(nèi)) -->
    │       ├─ 二級菜單標題
    │       └─ <el-menu-item>  <!-- 二級菜單子項 -->
    ├─ <el-menu-item>          <!-- 一級菜單子項(非展開項) -->
    └─ <el-menu-item>          <!-- 一級菜單子項 -->
</el-menu>

完整實例代碼

<template>
  <el-row class="tac">
    <el-col :span="12">
      <h5 class="mb-2">Default colors</h5>
      <el-menu
        default-active="2"
        class="el-menu-vertical-demo"
        @open="handleOpen"
        @close="handleClose"
      >
        <el-sub-menu index="1">
          <template #title>
            <el-icon><location /></el-icon>
            <span>Navigator One</span>
          </template>
          <el-menu-item-group title="Group One">
            <el-menu-item index="1-1">item one</el-menu-item>
            <el-menu-item index="1-2">item two</el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title="Group Two">
            <el-menu-item index="1-3">item three</el-menu-item>
          </el-menu-item-group>
          <el-sub-menu index="1-4">
            <template #title>item four</template>
            <el-menu-item index="1-4-1">item one</el-menu-item>
          </el-sub-menu>
        </el-sub-menu>
        <el-menu-item index="2">
          <el-icon><icon-menu /></el-icon>
          <span>Navigator Two</span>
        </el-menu-item>
        <el-menu-item index="3" disabled>
          <el-icon><document /></el-icon>
          <span>Navigator Three</span>
        </el-menu-item>
        <el-menu-item index="4">
          <el-icon><setting /></el-icon>
          <span>Navigator Four</span>
        </el-menu-item>
      </el-menu>
    </el-col>
  </el-row>
</template>
<script lang="ts" setup>
import {
  Document,
  Menu as IconMenu,
  Location,
  Setting,
} from '@element-plus/icons-vue'
const handleOpen = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
}
</script>

2.2 接口定義

定義組件的對象數(shù)組結構。path路徑,name名稱,label標簽,icon圖標,這四個屬性是必須的。個別對象本身有children屬性包含它的子菜單內(nèi)容。

export interface NavTreeMenuItem {
  path: string;
  name?: string;
  label: string;
  icon: string;
  children?: NavTreeMenuItem[];
}

2.3 組件遞歸

vue3中實現(xiàn)遞歸組件是組件在其模板中直接調用自身。比如我定義了一個組件名稱叫menuTreeItem.vue,我就可以在該vue文件內(nèi)直接調用menuTreeItem。這是基于這主要歸功于Vue 3 單文件組件的編譯時處理機制和 < script setup> 的特性。

在menuTreeItem.vue的el-sub-menu節(jié)點里判斷children屬性是否有值,并且沒有超過最大遞歸深度。

值得注意的是,使用遞歸方法要注意無限遞歸這個漏洞風險,需要設置最大深度,避免無限遞歸

menuTreeItem.vue文件片段

<template>
    <template v-for="item in treeData" :key="item.path" :index="item.path">
    	<el-sub-menu v-if="item.children && item.children.length > 0 && currentDepth < maxDepth"  :index="item.path">
	        <!--  -->
	        <menuTreeItem :tree-data="item.children"  :current-depth="currentDepth + 1"
	                :max-depth="maxDepth"></menuTreeItem>
	        <!--  -->
	     </el-sub-menu>
    </template>
</template>

2.4 父組件封裝遞歸組件

前文我們把需要遞歸的組件封裝成單獨子組件,最后嵌套在父組件里,防止不必要的內(nèi)容被遍歷渲染。

<template>
  <el-aside :style="{ width: menuWidth }">
    <h3 class="mb-2" v-once>后臺系統(tǒng)</h3>
    <div class="sidebar-menu">
      <el-menu default-active="2" class="my-el-menu" @open="handleOpen" @close="handleClose">
        <menuTreeItem :tree-data="treeData" :max-depth="5"></menuTreeItem>
      </el-menu>
    </div>
  </el-aside>
</template>

三、完整代碼——基于element-plus UI的多級導航欄

3.1 組件架構

navigationAside              
    ├─ index.vue			<!-- 核心組件 -->
    ├─ menuTreeItem.vue		<!-- 遞歸組件 -->
    └─ types.ts         	<!-- 接口類型 -->
</el-menu>

3.2 types.ts

export interface NavTreeMenuItem {
  path: string;
  name?: string;
  label: string;
  icon: string;
  url?: string;
  children?: NavTreeMenuItem[];
}

3.3 menuTreeItem.vue

<template>
    <template v-for="item in treeData" :key="item.path" :index="item.path">
        <el-sub-menu v-if="item.children && item.children.length > 0 && currentDepth < maxDepth"  :index="item.path">
            <template #title>
                <component class="icons" :is="item.icon"></component>
                <span>{{ item.label }}</span>
            </template>
            <MenuTreeItem :tree-data="item.children" :isRoot="false" :current-depth="currentDepth + 1"
                :max-depth="maxDepth"></MenuTreeItem>
        </el-sub-menu>
        <el-menu-item v-else :index="item.path">
                <component class="icons" :is="item.icon"></component>
            <span>{{ item.label }}</span>
        </el-menu-item>
    </template>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
const router = useRouter()
import {
    Document,
    Menu as IconMenu,
    Location,
    Setting,
} from '@element-plus/icons-vue'
import { NavTreeMenuItem } from './types'
const props = withDefaults(defineProps<{
    treeData: NavTreeMenuItem[];
    isRoot?: boolean;
    currentDepth?: number; // 當前深度
    maxDepth?: number;    // 最大深度
}>(), {
    isRoot: true,
    currentDepth: 1, // 默認當前深度為1
    maxDepth: 3 // 默認最大深度為3
})
</script>
<style lang="less" scoped>
:deep(.icons) {
    height: 18px;
    margin-right: 5px;
    width: 18px;
}
</style>

3.4 index.vue

<template>
  <el-aside :style="{width: menuWidth}">
    <h3 v-if="!isCollapse" class="mb-2" v-once>天津城安遠傳系統(tǒng)</h3>
    <h3  v-else class="mb-2" v-once>遠傳</h3>
    <div class="sidebar-menu">
      <el-menu  default-active="2"
        class="my-el-menu" 
        :collapse="isCollapse"
        @open="handleOpen" 
        @close="handleClose" >
        <menuTreeItem :tree-data="treeData" :max-depth="5"></menuTreeItem>
      </el-menu>
    </div>
  </el-aside>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import {useWebSettingDataStore} from '../../stores'
import menuTreeItem from './menuTreeItem.vue'
const router = useRouter()
import {
  Document,
  Menu as IconMenu,
  Location,
  Setting,
} from '@element-plus/icons-vue'
import { NavTreeMenuItem } from './types'
const props = defineProps<{
  treeData: NavTreeMenuItem[];
  isRoot?: boolean;
}>();
const store = useWebSettingDataStore()
const isCollapse = computed(() => store.state.isCollapse);
const menuWidth = computed(() => store.state.isCollapse ? '64px' : '180px')
const handleOpen = (key: string, keyPath: string[]) => {
  console.log("open")
  console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
  console.log("close")
  console.log(key, keyPath)
}
</script>
<style lang="less" scoped>
.el-menu {
  border-right: none;
  height: 100%;
}
.el-aside {
  display: flex;
  flex-direction: column;
  //border-right: 1px solid #e4e7ed;
  background-color: #304156;
  h3 {
    line-height: 13px;
    color: #fff;
    text-align: center;
    flex-shrink: 0;
    /* 防止被壓縮 */
  }
}
.sidebar-menu {
  flex: 1;
  overflow-y: auto;
  padding: 10px 0;
}
/* 可選:美化滾動條 */
.sidebar-menu::-webkit-scrollbar {
  width: 4px;
}
.sidebar-menu::-webkit-scrollbar-thumb {
  background-color: #bfcbd9;
  border-radius: 3px;
}
.my-el-menu{
  --el-menu-bg-color: #304156;
  --el-menu-text-color: #bfcbd9;
  --el-menu-hover-bg-color: #263445;
  --el-menu-active-color: #409eff;
}
</style>

3.5 組件調用與運行結果

<commNavigationAside :tree-data="navTreeData"></commNavigationAside>
import commNavigationAside from "@/components/navigationAside/index.vue"

總結

本文基于 Vue3 和 Element Plus,通過遞歸組件實現(xiàn)了可動態(tài)渲染的多級導航欄,利用自引用數(shù)據(jù)結構和深度控制避免無限循環(huán),同時結合 TypeScript 規(guī)范數(shù)據(jù)類型并優(yōu)化組件封裝。

到此這篇關于基于Vue3和Element Plus的遞歸組件實現(xiàn)多級導航欄的文章就介紹到這了,更多相關vue Element Plus導航欄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Vue初始化頁面閃動(FOUC)問題的終極解決方案

    Vue初始化頁面閃動(FOUC)問題的終極解決方案

    你是否遇到過頁面加載瞬間,用戶看到滿屏的 {{ message }}、{{ user.name }} 等未編譯的模板標簽,幾毫秒后才恢復正常,這就是 Vue 初始化閃動問題,也稱為 FOUC,本文將提供完整、可靠、可落地的解決方案,徹底根除此問題,需要的朋友可以參考下
    2025-10-10
  • Vite前端API調用與本地開發(fā)代理配置策略詳解

    Vite前端API調用與本地開發(fā)代理配置策略詳解

    本文詳細介紹了如何在前端代碼中使用相對路徑定義后端API URL,并通過Vite開發(fā)服務器的代理配置,解決了跨域資源共享(CORS)問題和環(huán)境差異問題,通過這套組合方案,實現(xiàn)了同一份前端代碼在本地開發(fā)和生產(chǎn)環(huán)境下的無縫運行
    2025-12-12
  • vue如何在用戶要關閉當前網(wǎng)頁時彈出提示的實現(xiàn)

    vue如何在用戶要關閉當前網(wǎng)頁時彈出提示的實現(xiàn)

    這篇文章主要介紹了vue如何在用戶要關閉當前網(wǎng)頁時彈出提示的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • vue訪問未定義的路由時重定向404問題

    vue訪問未定義的路由時重定向404問題

    這篇文章主要介紹了vue訪問未定義的路由時重定向404問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Element使用el-table組件二次封裝

    Element使用el-table組件二次封裝

    這篇文章主要為大家介紹了Element使用el-table組件二次封裝示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 淺談Vue.js中的v-on(事件處理)

    淺談Vue.js中的v-on(事件處理)

    本篇文章主要介紹了Vue.js中的v-on(事件處理),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • vue3實現(xiàn)按鈕級別權限控制的兩種方法

    vue3實現(xiàn)按鈕級別權限控制的兩種方法

    本文給大家介紹vue3實現(xiàn)按鈕級別權限控制的兩種方法,每種方法結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-08-08
  • Vue.js數(shù)字輸入框組件使用方法詳解

    Vue.js數(shù)字輸入框組件使用方法詳解

    這篇文章主要為大家詳細介紹了Vue.js數(shù)字輸入框組件的使用方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • Vue封裝的組件全局注冊并引用

    Vue封裝的組件全局注冊并引用

    這篇文章主要為大家詳細介紹了Vue封裝的組件全局注冊并引用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • vue?router?動態(tài)路由清除方式

    vue?router?動態(tài)路由清除方式

    這篇文章主要介紹了vue?router?動態(tài)路由清除方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05

最新評論

安庆市| 德州市| 会理县| 梁河县| 中江县| 凤阳县| 同德县| 屏南县| 康乐县| 朝阳区| 原阳县| 山丹县| 平顶山市| 上犹县| 昆明市| 兴和县| 德令哈市| 长岭县| 敦化市| 德安县| 方城县| 定陶县| 壤塘县| 南川市| 那坡县| 凤翔县| 融水| 阿瓦提县| 和田县| 景洪市| 汤阴县| 定州市| 老河口市| 木兰县| 霍城县| 马尔康县| 桦南县| 泸水县| 平山县| 栖霞市| 平昌县|