Vue - 实现Tree树形组件

1,975 阅读2分钟

前言

  在实际生活中经常会遇到树状列表,这里我们试着自己动手实现一个Tree树形组件。

主要思想

  对Vue组件使用递归,判断节点是否包含子节点,若包含则对组件进行递归。

开发环境及开发工具

  1. 开发工具:VS Code;
  2. 框架:Vue
  3. 图标库:Font Awesome

具体代码

    <div id="app">
        <sb-tree :data="list1"></sb-tree>
        <sb-tree :data="list2" :keys="keys"></sb-tree>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('sb-tree', {
            props: {
                data: {
                    type: Array,
                    required: true
                },
                keys: {
                    type: Object,
                    validator: function(val) {
                        let keys = Object.keys(val);
                        return keys.includes('label')&&keys.includes('children');
                    },
                    default () {
                        return {
                            label: 'label',
                            children: 'children'
                        }
                    }
                }
            },
            data() {
                return {
                    mylist: this.data.map(r=>{
                        return {
                            label: r[this.keys.label],
                            children: r[this.keys.children],
                            isShow: false
                        }
                    })
                }
            },
            // 版本2,可自定义数组属性名
            template: `
            <ul>
                <li v-for="item in mylist" style="margin:10px;list-style:none;">
                    <i class="fa" aria-hidden="true" v-if="item.children&&item.children.length" :class="item.isShow?'fa-angle-down':'fa-angle-right'"></i>
                    <span @click="item.isShow=!item.isShow">{{item.label}}</span>
                    <sb-tree style="padding:5px" v-show="item.isShow" :keys="keys" v-if="item.children&&item.children.length" :data="item.children"></sb-tree>
                </li>
            </ul>
            `,
            // 版本1,数组属性名必须为label和children
            // template: `
            // <ul>
            //     <li v-for="item in data">
            //         {{item.label}}
            //         <sb-tree v-if="item.children&&item.children.length" :data="item.children"></sb-tree>
            //     </li>
            // </ul>
            // `,

        })

        new Vue({
            el: "#app",
            data() {
                return {
                    keys: {
                        label: 'name',
                        children: 'cc'
                    },
                    list2: [{
                            name: 'A',
                            cc: [{
                                name: 'A-1',
                                cc: [{
                                    name: 'A-1-1',
                                    cc: [{
                                        name: 'A-1-1-1',
                                        cc: []
                                    }]
                                }, ]
                            }]
                        },
                        {
                            name: 'B',
                            cc: [{
                                name: 'B-1',
                                cc: []
                            }]
                        },
                    ],
                    list1: [{
                            label: 'A',
                            children: [{
                                    label: 'A-1',
                                    children: []
                                },
                                {
                                    label: 'A-2',
                                    children: [{
                                        label: 'A-2-1',
                                        children: []
                                    }, ]
                                },
                            ]
                        },
                        {
                            label: 'B',
                            children: []
                        },
                        {
                            label: 'C',
                            children: []
                        },
                    ],
                }
            },
            methods: {

            },
        })
    </script>

  组件sb-tree需要数据作为参数,这里利用propsdata作为必须的属性,keys作为可选属性。

  keys的作用是让调用者可以自由定义组件的节点名和子节点名。比如本例中list1labelchildrenlist2中就使用了自定义属性名namecc

  为了不改动原数据,在组件中使用mylist将原数据拷贝并处理,并添加一个isShow属性用来显示或隐藏子节点。

  效果如下图:

  点击节点时,会触发click事件,若isShow为false会将isShow取反并显示子节点,反之,若isShow为true则会将isShow取反为false并隐藏子节点。