Vue遞迴元件實現樹形結構選單

li_jing發表於2019-03-25

    Tree 元件是遞迴類元件的典型代表,它常用於資料夾、組織架構、生物分類、國家地區等等,世間萬物的大多數結構都是樹形結構。使用樹控制元件可以完整展現其中的層級關係,並具有展開收起選擇等互動功能。
    如圖所示,我們要實現的就是這樣一個效果。之前我們寫樹狀結構都是用jQuery來實現的,用Vue怎麼實現呢?

Vue遞迴元件實現樹形結構選單

一、資料部分模擬

  menuList:[
            {
                title:'選單1',
                children:[
                    {
                        title:'選單1-1',
                        children:[
                                {title:'選單1-1-1'},
                                {title:'選單1-1-2'},
                                {title:'選單1-1-3'}
                            ]
                        },
                    {title:'選單1-2'},
                    {title:'選單1-3'}
                ]
            },
            {title:'選單2'},
            {title:'選單3'}
    ]
  
複製程式碼

二、元件各部分實現

Menu.vue

首先我們來寫個menu元件,這裡放個ul列表,裡面的內容,用插槽來表示。
<template>
    <ul>
       <slot></slot>
    </ul>
</template>

<script>
    export default {
        name: "Menu"
    }
</script>
複製程式碼

MenuItem.vue

如果沒有子節點,所要展示的標題
<template>
    <li><slot></slot></li>
</template>

<script>
    export default {
        name: "MenuItem"
    }
</script>
複製程式碼

SubMenu.vue

<template>
      <div>
          <div class="title" @click="change">
              <slot name="title"></slot>
              <!--name='title'區分插槽 -->
          </div>
          <div class="sub" v-show="flag">
              <slot></slot>
          </div>
      </div>
</template>

<script>
    export default {
        name: "SubMenu",
        data(){
            return {flag:false}
        },
        methods:{
            change(){
                this.flag=!this.flag
            }
        }
    }
</script>

<style>
.sub{
    padding-left:20px;
}
</style>
複製程式碼

三、遞迴元件ReSubMenu.vue

這裡到了我們實現樹形結構思想的重點,即遞迴元件。當我們重複判斷有沒有子節點,並做出相應的展示的時候,這裡就可以使用遞迴元件了。方便快捷,你值得擁有。

由於有子節點會迴圈SubMenu這部分操作,所以單獨提出來放到ReSubMenu元件中
<template>
    <SubMenu>
        <template #title><!--#title為了標識區分插槽-->
            {{data.title}}
        </template>
        <template v-for="child in data.children">
            <MenuItem :key="child.title" v-if="!child.children">{{child.title}}</MenuItem>
            <!--ReSubMenu跟name的名字保持一致,相當於迴圈使用該元件-->
            <ReSubMenu v-else :key="child.title" :data="child"></ReSubMenu>
        </template>
    </SubMenu>
</template>

<script>
    import SubMenu from './SubMenu'
    import MenuItem from './MenuItem'
    export default {
        name: "ReSubMenu",//可以使用遞迴元件
        props:{
            data:{
                type:Object,//屬性校驗,為物件資料型別,並且如果沒有賦值,預設給一個空物件
                default:()=>({})
            }
        },
        components:{
            SubMenu,
            MenuItem
        }
    }
</script>
複製程式碼

四、整合實現

<div id="app">
   <Menu>
        <template v-for="menu in menuList">
                <MenuItem :key="menu.title" v-if="!menu.children">{{menu.title}}</MenuItem>
                <!--這部分是如果有孩子節點則會迴圈這部分操作,所以要單獨提取出來-->
               <!-- <SubMenu v-else>
                    <template #title>{{menu.title}}</template>
                    <template v-for="child in menu.children">
                        <MenuItem>{{child.title}}</MenuItem>
                    </template>
                </SubMenu>-->
                <ReSubMenu :key="menu.title" v-else :data="menu"></ReSubMenu>
        </template>
   </Menu>
<div>
<script>
    import Menu from './Menu'
    import MenuItem from './MenuItem'
   // import SubMenu from './SubMenu'
    import ReSubMenu from './ReSubMenu'
    export default {
        data(){
            return{
            //這裡的資料我就模擬一個了
                menuList:[
                    {
                        title:'選單1',
                        children:[
                            {
                                title:'選單1-1',
                                children:[
                                    {title:'選單1-1-1'},
                                    {title:'選單1-1-2'},
                                    {title:'選單1-1-3'}
                                ]
                            },
                            {title:'選單1-2'},
                            {title:'選單1-3'}
                        ]
                    },
                    {title:'選單2'},
                    {title:'選單3'}
                ]
            }
        },
        components:{
            Menu,MenuItem,ReSubMenu
        }
    }
</script>
複製程式碼

注:本節部分語句參考 https://juejin.im/book/5bc844166fb9a05cd676ebca/section/5bfcecc7f265da61682b102a

相關文章