何为递归
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
简单理解:函数内部自己调用自己, 这个函数就是递归函数
注意: 递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。
递归在实际中的作用
递归扁平化数据 转成tree结构
递归深拷贝
递归求和
递归做数据的增删改查
递归深拷贝
// 深度克隆
const deepClone = (value) => {
// 判断是数组 递归复制出去
if (Array.isArray(value)) {
const clone = []
for (let i = 0; i < value.length; i++) {
clone[i] = deepClone(value[i]);
}
return clone
}
// 如果是对象同理
if (typeof value === 'object' && typeof value !== null) {
const obj = {}
for (var key in obj) {
obj[key] = deepClone(value[key])
}
return obj
}
// 原值直接返回
return value
}
递归求和
function sum (nums) {
function getArr (param) {
return param >= nums.length ? 0 : nums[param] + getArr(param + 1)
}
return getArr(0)
}
接着就讲讲 今天用到的地方
- 页面的功能是这样的
- 需求是点击移除要移除数据 数据移除后按钮显示恢复 点击恢复又要恢复对应的数据
- 这里其实功能可以用显示隐藏来做 但是由于我用了树形结构 懒得写页面了 所以没有这样做
页面结构
<el-tree :data="data.nodeMenuList" :props="defaultProps" node-key="id" show-checkbox :default-expanded-keys="[1]" @check="checkPermission">
<template #default="{ node, data }">
<div class="custom-tree-node">
<span>{{ node.label }} {{ data.children.length }}</span>
<span class="icons-box" v-if="node.label != 'admin'">
<span class="icons-box" @click.stop="handleBtn(data, 'delete')" v-if="data.children.length > 0">
<el-icon size="18">
<icons.Minus />
</el-icon>
</span>
<span class="icons-box" @click.stop="handleBtn(data, 'add')" v-else>
<el-icon size="18">
<icons.Plus />
</el-icon>
</span>
</span>
</div>
</template>
</el-tree>
删除逻辑
拿到点击的数据 查找id是否存在 存在添加到删除数组;
不存在表示当前节点不是需要删除的节点, 即递归调用自身,继续对当前节点的子节点进行遍历;
这是为了深度遍历整个树结构,找到所有需要删除的节点。
- 恢复节点的逻辑稍微复杂一点点
首先要拿到恢复节点的id
其次 要有个选定节点的数组matchingNodes 目的是用来匹配被删除的数组 保存需要恢复的节点
跟删除一样 递归查找id存在吗
恢复节点 同是要删除对应删除数组里面的数据
代码 逐行解释
const deleteNodeAndChildren = (parentNode: any, nodeId: any, deletedNodes: any) => {
if (!parentNode.children || parentNode.children.length === 0) return
parentNode.children = parentNode.children.filter((childNode: any) => {
if (childNode.fid === nodeId) { // 查找id 如果父级id相同那么是用一个数据 就添加到删除数组
data.deletedNodes.push(childNode)
return
}
deleteNodeAndChildren(childNode, nodeId, deletedNodes) // 递归调用数据 查找所有的数据
return
})
}
// 删除节点
const handleDeleteNode = (nodeData: any) => {
deleteNodeAndChildren(nodeData, nodeData.id, data.deletedNodes)
}
// 恢复节点 node:点击的每个子节点数据
const handleRestoreNodes = () => {
// selectedNodeToRestore这是点击恢复的id
if (selectedNodeToRestore.value !== null) {
// 递归查找所有匹配的节点
const findMatchingNodes = (nodes: any, fid: any, result: any) => {
for (const node of nodes) {
if (node.fid === fid) { // 同理也是拿到id比对
result.push(node)
}
if (node.children && node.children.length > 0) { // 这句可加可不加
findMatchingNodes(node.children, fid, result)
}
}
}
const matchingNodes = [] as any
findMatchingNodes(data.deletedNodes, selectedNodeToRestore.value, matchingNodes)
if (matchingNodes.length > 0) {
// 恢复所有匹配的节点
for (const selectedNode of matchingNodes) {
handleAddNode(selectedNode)
}
selectedNodeToRestore.value = null // 恢复后删除节点
}
}
}
// 恢复节点操作 nodeID:点击的id parentNode:里面的每一条数据
const handleAddNode = (parentNode: any) => {
const nodeId = parentNode.fid // 点击的id
const findAndAddNode = (items: any[], parentId: number) => {
for (const item of items) {
if (item.id === parentId) {
item.children.push(parentNode)
// 从deletedNodes中删除对应的节点
data.deletedNodes = data.deletedNodes.filter((node: any) => node.id !== parentNode.id)
return true // 添加节点退出递归
}
if (item.children && findAndAddNode(item.children, parentId)) {
return true
}
}
return false // 没有节点
}
// 恢复节点
findAndAddNode(data.nodeMenuList, nodeId)
}
const handleBtn = (data: any, type: string) => {
console.log(data, type)
if (type === 'delete') {
handleDeleteNode(data)
} else if (type === 'add') {
selectedNodeToRestore.value = data.id
handleRestoreNodes()
}
}
数据格式
arr:[
0:{
children:[
{
"id": 2,
"name": "admin",
"title": "权限设置",
"fid": 1,
"type": 1,
"status": 1,
"sort": 1,
"condition": "",
"create_time": "2018-07-11 18:24:04",
"update_time": "2019-06-18 11:53:38",
"children": [
{
"id": 3,
"name": "admin/1",
"title": "管理权限",
"fid": 2,
"type": 1,
"status": 1,
"sort": 0,
"condition": "",
"create_time": "2018-07-11 18:24:04",
"update_time": "2019-06-18 11:56:00",
"children": [
{
"id": 4,
"name": "admin/group",
"title": "权限组列表",
"fid": 3,
"type": 1,
"status": 1,
"sort": 10,
"condition": "",
"create_time": "2018-07-11 18:24:04",
"update_time": "2019-06-18 15:13:44",
"children": [
{
"id": 10,
"name": "admin/groupadd",
"title": "增加",
"fid": 4,
"type": 1,
"status": 1,
"sort": 3,
"condition": "",
"create_time": "2018-07-13 10:46:23",
"update_time": "2019-06-18 15:13:57",
"children": []
},
{
"id": 11,
"name": "admin/groupedit",
"title": "修改",
"fid": 4,
"type": 1,
"status": 1,
"sort": 2,
"condition": "",
"create_time": "2018-07-13 10:46:52",
"update_time": "2019-06-18 15:14:05",
"children": []
},
{
"id": 12,
"name": "admin/groupdel",
"title": "删除",
"fid": 4,
"type": 1,
"status": 1,
"sort": 1,
"condition": "",
"create_time": "2018-07-13 10:47:24",
"update_time": "2019-06-18 15:14:12",
"children": []
}
]
},
{
"id": 8,
"name": "admin/index",
"title": "管理员列表",
"fid": 3,
"type": 1,
"status": 1,
"sort": 9,
"condition": "",
"create_time": "2018-07-11 19:40:04",
"update_time": "2019-06-18 15:16:12",
"children": [
{
"id": 9,
"name": "admin/manageradd",
"title": "添加",
"fid": 8,
"type": 1,
"status": 1,
"sort": 3,
"condition": "",
"create_time": "2018-07-12 19:15:55",
"update_time": "2019-06-18 15:16:25",
"children": []
},
{
"id": 13,
"name": "admin/manageredit",
"title": "修改",
"fid": 8,
"type": 1,
"status": 1,
"sort": 2,
"condition": "",
"create_time": "2018-07-13 10:49:05",
"update_time": "2019-06-18 15:16:33",
"children": []
},
{
"id": 14,
"name": "admin/managerdel",
"title": "删除",
"fid": 8,
"type": 1,
"status": 1,
"sort": 1,
"condition": "",
"create_time": "2018-07-13 10:49:28",
"update_time": "2019-06-18 15:16:40",
"children": []
}
]
},
{
"id": 15,
"name": "admin/logg",
"title": "后台操作日志",
"fid": 3,
"type": 1,
"status": 1,
"sort": 7,
"condition": "",
"create_time": "2018-07-13 10:50:43",
"update_time": "2019-06-18 15:16:49",
"children": [
{
"id": 16,
"name": "admin/loggdel",
"title": "删除",
"fid": 15,
"type": 1,
"status": 1,
"sort": 1,
"condition": "",
"create_time": "2018-07-13 10:51:19",
"update_time": "2019-06-18 15:16:56",
"children": []
},
{
"id": 18,
"name": "admin/loggdelall",
"title": "清空",
"fid": 15,
"type": 1,
"status": 1,
"sort": 0,
"condition": "",
"create_time": "2018-07-14 15:31:54",
"update_time": "2019-06-18 15:17:03",
"children": []
}
]
},
{
"id": 5,
"name": "admin/node",
"title": "节点管理(管理员)no",
"fid": 3,
"type": 1,
"status": 1,
"sort": 0,
"condition": "",
"create_time": "2018-07-11 18:24:04",
"update_time": "2018-07-20 10:40:03",
"children": [
{
"id": 7,
"name": "admin/nodeadd",
"title": "添加",
"fid": 5,
"type": 1,
"status": 1,
"sort": 3,
"condition": "",
"create_time": "2018-07-11 18:14:41",
"update_time": "2019-06-18 15:19:37",
"children": []
},
{
"id": 6,
"name": "admin/nodeedit",
"title": "修改",
"fid": 5,
"type": 1,
"status": 1,
"sort": 2,
"condition": "",
"create_time": "2018-07-11 15:03:54",
"update_time": "2019-06-18 15:19:43",
"children": []
},
{
"id": 17,
"name": "admin/nodedel",
"title": "删除",
"fid": 5,
"type": 1,
"status": 0,
"sort": 1,
"condition": "",
"create_time": "2018-07-13 10:52:37",
"update_time": "2019-06-18 15:19:51",
"children": []
}
]
}
]
}
]
}
]
condition: ""
create_time: "2018-07-11 18:03:36"
fid: 0
id: 1
name: "root"
sort: 100
status: 1
title: "admin"
type: 1
update_time: "2023-12-08 17:23:53"
}
]
结尾
如果有遇到同样功能的小伙伴想要用递归来做的可以使用这段代码 同时也欢迎各位大佬指点 这里还有一个不完善的地方 例如我们删除全局下的某一个节点 然后再把全局节点删除 接着恢复全局节点 这时候被删除的节点数据没有恢复需要我们手动去恢复 这个问题有大佬能解答吗