前言
看到最近一些公司前端笔试题,发现他们都很喜欢考察递归。这使我不得不想到在前端开发中,也会遇到的一些需要利用递归思想实现的一些场景,如目录树组件,大多数前端开发经常参与流水型业务,对组件递归的场景用之较少。以下为作者根据实践,分享递归组件实现目录树的设计思路以及实现方式。我们要实现一个目录组件,效果如下。
设计思路
在真实业务中,treeList是后端返回的,可能有数十个层级。这里为了方便我模拟了三层数据,旨在清晰逻辑,数据以及结构如下:
treeList: [
{
id: 1,
name: '一级1',
children: [
{
id: 2,
name: '二级1',
children: [
{
id: 3,
name: '三级1'
},
{
id: 4,
name: '三级2'
}
]
},
{
id: 5,
name: '二级2'
}
]
},
{
id: 6,
name: '一级2',
children: [
{
id: 7,
name: '二级3'
}
]
}
]
从数据结构中可以发现,外层为一个数组,每一项有id作为唯一标识,name作为展示数据,children为子数组,包含类似外层的结构。抽象成“树”的形式如下图。
根据上图基本图形化数据结构,若要实现点击选中的效果需要设计数据流。
- 初始化时,将activeKey传入Tree组件,通过层层传递到各个子组件,使用动态class的方式用逻辑item.id === activeKey判断是否为选中状态。
- 选中某一个节点,则触发change事件,层层往上抛,最终在调用者处理activeKey的赋值逻辑形成闭环。
这里为什么不在数据中注入selected标识来标记是否选中?有以下几点:
- 注入selected标识会对源数据造成风险,如果selected被定义在接口返回值中,则会对源数据进行污染。
- 注入selected会增加逻辑复杂度,增加维护成本;因为选中后,不仅是修改当前元素的selected,还要遍历整个list,将selected初始化为false后方可修改。
经过以上设计,我们大致可以得出组件调用的逻辑图。
实现过程
我们需要创建两个组件,一个调用者TreeTest组件,另一个为树组件Tree。通过TreeTest去调用Tree,Tree内部递归。
对于TreeTest组件,我们希望他是一个Tree组件的调用者,同时也是选中态的“数据中心”。
<Tree :list="treeList" :activeKey="activeKey" @change="handleChange"/>
对于Tree组件,我们希望将递归逻辑全部在Tree组件内部实现,与外部的数据和逻辑解耦,从而提高代码的可维护性。(Tree内部调用自身不用import自己,而使用name属性与标签中Tree保持一致即可)
<div class="tree">
<div v-for="item in list" :key="item.id">
<div class="name" :class="{ 'active': activeKey === item.id }" @click="changeItem(item)">{{ item.name }}</div>
<Tree :list="item.children" :activeKey="activeKey" v-if="item.children" @change="changeItem"/>
</div>
</div>
总结
到这里我们基本实现了用组件递归的方式去开发目录树组件,代码非常简单,不过思想值得关注。希望能够用本篇博客给正在的浏览的您一些灵感,如果对您有帮助可以点点关注哦,如果有好的建议或者意见可以评论或者私信,感谢您的支持!
PS:相关代码已绑定本文资源,可以免费下载,希望对您有帮助。