树状图封装.vue

封装准备 完整代码可直接置底查看

组件的封装来源于数据

我们把树状图(下文统称为"图")的每一个节点作为一个看作一个对象,它将需要的节点属性如下:

属性名称 作用
id 唯一标识
title 节点名称
children 子集
expend 展开控制变量
js 复制代码
//数据准备
let treeData = [
    {
        id: 'treeRoot',
        title: '目录根节点',
        expend: false,
        children: [
            {
                id: 'subNode',
                title: '目录子节点',
            }
        ],
    },
];

开始封装

我们确认了图的数据是一个可以无限递归的层级数据,所以我们首先需要封装一个方法来遍历数据。

js 复制代码
//树状图数据遍历方法
function TreeNode(arr) {
    return arr.reduce((pre, cur) => {
        if (cur.children && Array.isArray(cur.children)) {
            cur.children = TreeNode(cur.children);
        }
        pre.push(cur);
        return pre;
    }, []);
}

其次是组件内部结构,我们需要用到递归组件,所谓的递归组件就是在vue单文件内部给予name属性,我们就可以在该文件内直接调用组件。

js 复制代码
<template>
    <div class="tree">
        <div class="tree-item" v-for="item in treeData" :key="item.id">
            <div>{{ item.title }}</div>
            <Tree :treeData="item.children || []" />
        </div>
    </div>
</template>

<script>
export default {
    name: 'Tree',
    props: {
        treeData: {
            default: [
                {
                    id: 'treeRoot',
                    title: '目录根节点',
                    expend: false,
                    children: [
                        {
                            id: 'subNode',
                            title: '目录子节点',
                        },
                    ],
                },
            ],
        },
    },
};
</script>

视图效果

接下来我们为每一级节点添加缩进,我们定义一个初始值pad,当用户调用时未传pad,此时采用默认值,pad为0,接下来每一级逐级+1传递,即可实现节点缩进效果。

<Tree :treeData="item.children" :pad="pad+1">

js 复制代码
pad: {
    type: Number,
    default: 0,
},

当数据多出之后,我们需要为图添加展开/收起功能,用expend控制Tree 展示隐藏即可

<Tree :treeData='item.children' v-if="item.expend" :pad="pad+1">

我们可以添加一些图标或者标签来当作操作按钮,这里不予多做解释。至于不定高度的展开收起动画,我采用grid的template-rows:1fr/0fr;来实现动画的高度变化。

Css 复制代码
.tree-sub {
    display: grid;
    transition: all 0.3s;
}
.tree-item-expend {
    grid-template-rows: 1fr;
}
.tree-item-hidden {
    grid-template-rows: 0fr;
}

此后,树状图的已具有初步效果,我们还需要再继续进行一些细微之处的修改。各位同学可以通过CV查看具体效果或继续进行封装修改。

完整代码

Vue 复制代码
<template>
    <div class="tree">
        <div class="tree-item" v-for="item in treeData" :key="item.id">
            <div class="tree-item-row" :style="`padding-left:${pad}rem`">
                <div class="tree-item-row-opr" @click.stop="item.expend = !item.expend" v-if="item.children && Array.isArray(item.children)">
                    {{ item.expend ? '-' : '+' }}
                </div>
                <div class="tree-item-row-opr-none" v-else></div>
                <div class="tree-item-row-text">{{ item.title }}</div>
            </div>
            <div
                class="tree-sub"
                :class="{
                    'tree-item-expend': item.expend,
                    'tree-item-hidden': !item.expend,
                }"
            >
                <Tree :treeData="item.children || []" :pad="pad + 1" />
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'Tree',
    props: {
        treeData: {
            Type: Array,
            default: () => [
                {
                    id: 'treeRoot',
                    title: '目录根节点',
                    expend: false,
                    children: [
                        {
                            id: 'subNode1',
                            title: '目录子节点1',
                            expend: false,
                            children: [
                                {
                                    id: 'subNode11',
                                    title: '目录子节点1-1',
                                },
                                {
                                    id: 'subNode12',
                                    title: '目录子节点1-2',
                                },
                                {
                                    id: 'subNode13',
                                    title: '目录子节点1-3',
                                },
                                {
                                    id: 'subNode14',
                                    title: '目录子节点1-4',
                                },
                            ],
                        },
                        {
                            id: 'subNode2',
                            title: '目录子节点2',
                        },
                        {
                            id: 'subNode3',
                            title: '目录子节点3',
                        },
                        {
                            id: 'subNode4',
                            title: '目录子节点4',
                        },
                        {
                            id: 'subNode5',
                            title: '目录子节点5',
                        },
                        {
                            id: 'subNode6',
                            title: '目录子节点6',
                        },
                    ],
                },
            ],
        },
        pad: {
            type: Number,
            default: 0,
        },
    },
};
</script>

<style lang="less">
.tree {
    box-sizing: border-box;
    overflow: hidden;
    &-item {
        display: flex;
        flex-direction: column;
        gap: 4px;
        &-row {
            display: flex;
            gap: 4px;
            &-opr {
                cursor: pointer;
                width: 16px;
                font-size: 16px;
                height: 20px;
                line-height: 20px;
                text-align: center;
            }
            &-opr-none {
                cursor: default;
            }
        }
        .tree-sub {
            display: grid;
            transition: all 0.3s;
        }
        .tree-item-expend {
            grid-template-rows: 1fr;
        }
        .tree-item-hidden {
            grid-template-rows: 0fr;
        }
    }
}
</style>

Author: 王光英,目前于北京远舢负责前端开发工作,各位道友有什么前端经验都可一起分享。

组件封装为系列作品,树状图组件远远不止于此,开发还有特定需求,如关键字检索,目录检索,多选,单选,懒加载,标题超长缩略提示等。

文中提到的遍历数据方法为所有功能基座,所以单独提出。

文章更新时间视点赞量而定~

相关推荐
10年前端老司机33 分钟前
什么!纯前端也能识别图片中的文案、还支持100多个国家的语言
前端·javascript·vue.js
摸鱼仙人~36 分钟前
React 性能优化实战指南:从理论到实践的完整攻略
前端·react.js·性能优化
程序员阿超的博客2 小时前
React动态渲染:如何用map循环渲染一个列表(List)
前端·react.js·前端框架
magic 2452 小时前
模拟 AJAX 提交 form 表单及请求头设置详解
前端·javascript·ajax
小小小小宇6 小时前
前端 Service Worker
前端
只喜欢赚钱的棉花没有糖7 小时前
http的缓存问题
前端·javascript·http
小小小小宇7 小时前
请求竞态问题统一封装
前端
loriloy7 小时前
前端资源帖
前端
源码超级联盟7 小时前
display的block和inline-block有什么区别
前端
GISer_Jing7 小时前
前端构建工具(Webpack\Vite\esbuild\Rspack)拆包能力深度解析
前端·webpack·node.js