背景
最近有朋友说面试碰到了些树状数据结构渲染,还有在Vue和React中该怎么写树状组件的问题,恰巧作为菜逼的我以前有整理过一些题,还有自己的练习,先分享一波,有问题的小伙伴可以一起探讨
題目
概述
- 这边的数据,就拿我们当年的童年,war3举例子
- 本人之前一直玩的是兽族,四个英雄,各列举了点技能
- 然后就愉快的做题吧
练习
-
先来看下数据格式
let list = [ { id: 0, name: "兽族英雄技能", pid: -1 }, { id: 1, name: "剑圣", pid: 0 }, { id: 2, name: "牛头人酋长", pid: 0 }, { id: 3, name: "先知", pid: 0 }, { id: 4, name: "巫医", pid: 0 }, { id: 5, name: "疾风步", pid: 1 }, { id: 6, name: "分身", pid: 1 }, { id: 7, name: "致命一击", pid: 1 }, { id: 8, name: "剑刃风暴", pid: 1 }, { id: 9, name: "治疗波", pid: 4 }, { id: 10, name: "妖术", pid: 4 }, { id: 11, name: "毒蛇守卫", pid: 4 }, { id: 12, name: "无敌", pid: 4 }, { id: 13, name: "1级疾风步", pid: 5 }, { id: 14, name: "2级疾风步", pid: 5 }, { id: 15, name: "3级疾风步", pid: 5 } ]; -
完成以下题目
-
根据id获取对应的数据
-
根据id获取它的父亲
-
根据id获取它的孩子节点
-
根据id获取它所有的上级节点(父,爷...)
-
根据id获取它所有的下级节点(孩子,孙子...)
-
根据给定的list生成树状结构,类似下图
-
答案
获取自身
function getSelf(list, id){
return list.filter(item => item.id === id)[0];
}
// console.log(getSelf(list, 15));
获取父亲
function getParent(list, id){
let self = getSelf(list, id);
return getSelf(list, self.pid);
}
// console.log(getParent(list, 15));
获取孩子
function getChildren(list, id){
return list.filter(item => item.pid === id);
}
// console.log(getChildren(list, 5));
获取上级(父亲,爷爷...)
function getAllParent(list, id){
let parent = getParent(list, id);
let result = [];
while(parent){
result.unshift(parent);
parent = getParent(list, parent.id);
}
return result;
}
获取下级(孩子,孙子...)
function getAllChildren(list, id){
let children = getChildren(list, id);
let result = [].concat(children);
children.forEach(child => {
result = result.concat(getChildren(list, child.id));
})
return result;
}
// console.log(getAllChildren(list, 1));
列表转树状 - 递归实现
export function transListToTreeData(list, flag) {
const arr = []
list.forEach(item => {
if (item.pid === flag) {
// item => 一级部门
// 一级部门是有二级部门 应该是拿着一级部门的id 作为查找标记
// result 子部门数组
const result = transListToTreeData(list, item.id)
if (result.length > 0) {
item.children = result
}
arr.push(item)
}
})
return arr
}
树状转列表 - 递归实现
export function treeToList(tree) {
let newList = []
tree.forEach(item => {
newList.push(item)
// 判断item是否有children
if (item.children) {
const result = treeToList(item.children)
newList = [...newList, ...result]
delete item.children
}
})
return newList
}
Vue和React树组件
Vue的树组件小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tree demo</title>
</head>
<body>
<div id="app">
<h1>{{ title }}</h1>
<ul>
<items v-for="model in treeData" :key="model.id" :model="model"></items>
</ul>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.11/vue.min.js"></script>
<script>
Vue.component('items', {
props: ['model'],
name: 'items',
template: `
<li @click.stop="toggle">
<span>{{ model.title }}</span>
<ul v-if="hasChildren" v-show="open">
<items v-for="item in model.children" :key="item.id" :model="item" />
</ul>
</li>
`,
data () {
return {
open: true,
}
},
computed: {
hasChildren () {
return this.model.children && this.model.children.length
}
},
methods: {
toggle () {
if (this.hasChildren) {
this.open = !this.open
}
}
}
})
const vm = new Vue({
el: "#app",
data: {
title: "tree demo",
treeData: [
{
id: 1,
title: "node-1",
children: [
{
id: 2,
title: "node-2",
children: [
{
id: 3,
title: "node-3"
},
{
id: 4,
title: "node-4"
},
{
id: 5,
title: "node-5"
}
]
}
]
}
]
}
});
</script>
</body>
</html>
React树组件小案例
import React, { Component } from "react";
const treeData = {
title: "总览",
key: "big-title",
children: [
{
title: "0-0",
key: "0-0",
children: [
{
title: "0-0-0",
key: "0-0-0",
children: [
{ title: "0-0-0-0", key: "0-0-0-0" },
{ title: "0-0-0-1", key: "0-0-0-1" },
{ title: "0-0-0-2", key: "0-0-0-2" },
],
},
{
title: "0-0-1",
key: "0-0-1",
children: [
{ title: "0-0-1-0", key: "0-0-1-0" },
{ title: "0-0-1-1", key: "0-0-1-1" },
{ title: "0-0-1-2", key: "0-0-1-2" },
],
},
{
title: "0-0-2",
key: "0-0-2",
},
],
},
{
title: "0-1",
key: "0-1",
children: [
{ title: "0-1-0-0", key: "0-1-0-0" },
{ title: "0-1-0-1", key: "0-1-0-1" },
{ title: "0-1-0-2", key: "0-1-0-2" },
],
},
{
title: "0-2",
key: "0-2",
},
],
};
class TreeNode extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
};
}
get isFolder() {
return this.props.treeData.children && this.props.treeData.children.length;
}
toggle = () => {
this.setState({
isOpen: !this.state.isOpen
})
}
render() {
return (
<ul>
<li>
<div style={{cursor: 'pointer'}} onClick={this.toggle}>
{this.props.treeData.title}
<span>
{this.isFolder ? (this.state.isOpen ? " -" : " +") : null}
</span>
</div>
{this.isFolder ? (
<div style={{ display: this.state.isOpen ? "block" : "none" }}>
{this.props.treeData.children.map((item) => (
<TreeNode treeData={item} key={item.key} />
))}
</div>
) : null}
</li>
</ul>
);
}
}
class TreeTest extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Tree Demo</h1>
<TreeNode treeData={treeData} />
</div>
);
}
}
export default TreeTest;