想到树状数据, 其实大家都有点怵,原因其实也很简单, 数据的增删改查都很困难,需要一层一层的去遍历。 只要数据的层级和数量稍微大一点, 就很容易导致性能出现问题,让你的页面出现操作的明显卡顿。
那怎么解决这个问题, 毕竟很多场景里面, 都会遇到这样的数据结构。这个时候我们就要请出我们今天的主角 Map。
Map 简介
Map 数据类型是 ES6 新增的一种数据结构。它类似于 Object 结构, 也是键值对的集合, 那么 Map 与 Object 有什么不一样的地方呢?
- Map 的键值的类型可以是字符串也可以是其他类型, 包括一些复杂数据类型
- Map 是有顺序的, 也就是说, 它的遍历顺序跟它的插入顺序一致。
- 友好的 api, set 设置, get 获取, size 获取长度, has 判断是否存在。。。
上面我们简单的介绍了一下 Map 数据结构, 那它跟我们的树状结构有什么关系呢? 下面我们就来说说
拍平
我们知道, 多维的数据(树状结构数据), 每次操作某一项数据, 都得层层遍历。这个时候我们会无比想念一维的数据, 因为只需要遍历一次就行了,对每项数据的操作难度也是肉眼可见的降低。其实我们可不可以做到呢, 答案是显而易见的。这个时候我们就可以把树状结构的数据进行拍平, 转换成一维数据。
转换
转换后:
这里面我们可以看到, 我们顺利的把树状结构的数据转换成了一个一维数据。我们可以看到我们把所有的字节点的key保存在了 childrenKeys 数组中,方面后面直接遍历或者判断,同时我们将每个节点的父节点 key 也保存在了parentKey 中, 这样我们通过一个节点, 就可以找到这个节点的父节点和所有子节点。到这里我们就完成了第一步。
怎么快速获取数据
这个时候, 如果我想要获取 key 为 0_1_1 这一项的数据, 我们怎么做?
当然是遍历了, 数据不多的时候, 一点关系的都没有, 但是如果数据量很大呢, 1000 条数据呢, 我们每次操作前都要去获取数据 都遍历一次, 性能可想而知。
这个时候, 我们又在想, 能不能像对象一样, 可以拿着key直接去取数据, 而不必去遍历整个数组。答案当然是可以的。
我们改下上面的转换代码:
新增
在指定节点下新增一个节点item
// 假如指定节点的key: parentKey
flatMap.set(key, item)
flatMap[parentKey].childrenKeys.push(key)
获取
item = flatMapt.get(key)
修改
flatMap.set(key, ...) 或者 flatMap[key].name = '修改这个数据'
删除
根据一个key删除一个节点
// 判断是否存在这个节点
if(flatMap.has(key)) {
flatMap.delete(key)
flatMap[parentKey].childrenKeys = flatMap[parentKey].childrenKeys.filter(childKey => childKey !== key)
}
如何渲染树结构数据
树结构
树结构的渲染就很简单
// 渲染方法
const renderFunc = (data) => {
return data.map(node => {
if(node.children && node.children.length) {
return <div>
<div>渲染{node.name}</div>
{renderFunc(node.children)}
</div>
}
return <div>渲染{node.name}</div>
})
}
// 树数据遍历
renderFunc(treeData)
这样的渲染其实就是不停递归进行渲染
Map 结构
// 渲染方法
const renderFunc = (data) => {
if(!data) {
return null
}
const {childrenKeys} = data
return <div>
<div>渲染{node.name}</div>
{childrenKeys.map(key => {
return renderFunc(flatMap.get(key))
})}
</div>
})
}
// 从根节点开始渲染
renderFunc(faltMap.get('0'))
这里面的区别就是拿 key去获取节点展示,从根本上来说,其实渲染这块都是差不多的,都是递归
总结
树状结构转 Map 结构,主要的提升在于操作节点(增 删 改 查)的时候可以减少不必要的遍历,从而提升性能。