我们的国家国土面积十分的广阔,目前中国有 34 个省级行政区,包括 23 个省、5 个自治区、4 个直辖市、2 个特别行政区。其下面还有几千个县级区域,以及更多的乡镇区域。 这样层层嵌套的省市区数据在实际开发中是如何处理的呢?下面请运用所学,解开这个谜团吧~
本题已经内置了初始代码,打开实验环境,目录结构如下:
├── convert-to-tree.js
└── index.html
其中:
index.html
是主页面。convert-to-tree.js
是需要补充代码的 js 文件。在实际开发中,由于省市区等层级较多,使得数据结构按照嵌套关系来存储则过于复杂,一般在数据库中会将省市区数据解耦,每条信息以并列关系存储在一张表中,结构如下:
[
{
id: "51", // 区域 id
name: "四川省", // 区域名字
pid: "0", // 区域的父级区域 id
},
{
id: "5101",
name: "成都市",
pid: "51", // 成都的父级是四川省,所以 pid 是 51
},
// ...
];
各级行政区域通过 pid
关联起来。
但是这样平铺的数据放在前端页面展示的话,用户很难看出其中的层级关系,所以需要将平铺的结构转化为树状结构,便于前端展示:
[
{
id: "51", // 地址 id
name: "四川省", // 地址名
pid: "0", // 该地址的父节点 id
children: [
{
id: "5101",
name: "成都市",
pid: "51",
children: [
{
id: "510101",
name: "市辖区",
pid: "5101",
children: [], // 如果该区域节点没有子集,children 则为空数组!!!
},
// ...
],
},
// ...
],
},
// ...
];
补充文件 convert-to-tree.js
中的 convertToTree
工具函数,使其实现我们需要的功能:
rootId
,用于标识返回哪一个区域下面的所有节点。默认为 0
表示返回所有省份信息(在我们的示例数据中,所有省份的 pid
都为 0
)。完成后,最终页面效果如下:
convert-to-tree.js
中指定区域答题,不能修改 index.html
中的任何代码。convert-to-tree.js
以及 index.html
文件的文件路径以及文件名。function convertToTree(regions, rootId = "0") {
// 创建一个空对象,用于存储每个节点的引用
const nodeMap = {};
// 存储最终的树状结构
const tree = [];
// 遍历区域数组,将每个节点存储到 nodeMap 中,并初始化 children 属性
regions.forEach(region => {
// 复制一份区域信息,避免修改原始数据
const newRegion = { ...region, children: [] };
nodeMap[region.id] = newRegion;
});
// 再次遍历区域数组,建立节点之间的父子关系
regions.forEach(region => {
const currentNode = nodeMap[region.id];
const parentId = region.pid;
// 找到父节点
const parentNode = nodeMap[parentId];
if (parentNode) {
// 如果父节点存在,将当前节点添加到父节点的 children 数组中
parentNode.children.push(currentNode);
} else if (region.pid === rootId) {
// 如果父节点不存在且当前节点的父节点 id 等于 rootId,将当前节点添加到树的根节点
tree.push(currentNode);
}
});
return tree;
}
module.exports = convertToTree; // 检测需要,请勿删除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>平铺数据生成树状结构</title>
</head>
<style>
#output-container {
width: 600px;
}
</style>
<body>
<div id="output-container"></div>
<script>
module = {};
</script>
<script type="text/javascript" src="./convert-to-tree.js"></script>
<script>
const convertToTreeFn = module.exports;
const output = document.getElementById("output-container");
const regions = [
// 大量的区域数据对象,每个对象包含 id、name 和 pid
{
id: "51",
name: "四川省",
pid: "0",
},
// 其他区域数据...
];
const result = convertToTreeFn(regions);
output.appendChild(getRegionDoms(result));
function getRegionDoms(regions) {
if (!regions || regions.length === 0) {
return;
}
const ul = document.createElement("ul");
regions.forEach((region) => {
const li = document.createElement("li");
const textNode = document.createTextNode(region.name);
li.appendChild(textNode);
const children = getRegionDoms(region.children);
if (children) {
li.appendChild(children);
}
ul.appendChild(li);
});
return ul;
}
</script>
</body>
</html>
<head>
部分: id
为 output-container
的元素宽度设置为 600px。<body>
部分: div
元素,id
为 output-container
,用于显示最终的树状结构数据。module
对象,用于模拟 Node.js 中的模块导出机制。convert-to-tree.js
文件,该文件包含将平铺数据转换为树状结构的函数。convert-to-tree.js
文件中导出的 convertToTree
函数。regions
,每个区域对象包含 id
、name
和 pid
属性。convertToTreeFn
函数将平铺的区域数据转换为树状结构。getRegionDoms
函数将树状结构的数据转换为 HTML 列表,并将其添加到 output-container
元素中。getRegionDoms
函数: regions
数组为空或不存在,则返回 undefined
。ul
元素。regions
数组,为每个区域创建一个 li
元素,并将区域名称作为文本节点添加到 li
元素中。getRegionDoms
函数处理当前区域的子区域,并将结果添加到当前 li
元素中。li
元素添加到 ul
元素中,并返回 ul
元素。function convertToTree(regions, rootId = "0") {
// 创建一个空对象,用于存储每个节点的引用
const nodeMap = {};
// 存储最终的树状结构
const tree = [];
// 遍历区域数组,将每个节点存储到 nodeMap 中,并初始化 children 属性
regions.forEach(region => {
// 复制一份区域信息,避免修改原始数据
const newRegion = { ...region, children: [] };
nodeMap[region.id] = newRegion;
});
// 再次遍历区域数组,建立节点之间的父子关系
regions.forEach(region => {
const currentNode = nodeMap[region.id];
const parentId = region.pid;
// 找到父节点
const parentNode = nodeMap[parentId];
if (parentNode) {
// 如果父节点存在,将当前节点添加到父节点的 children 数组中
parentNode.children.push(currentNode);
} else if (region.pid === rootId) {
// 如果父节点不存在且当前节点的父节点 id 等于 rootId,将当前节点添加到树的根节点
tree.push(currentNode);
}
});
return tree;
}
module.exports = convertToTree; // 检测需要,请勿删除
convertToTree
函数接受两个参数:regions
是平铺的区域数据数组,rootId
是根节点的 id
,默认为 "0"
。nodeMap
是一个空对象,用于存储每个节点的引用,方便后续查找。tree
是一个空数组,用于存储最终的树状结构。regions
数组: children
属性。nodeMap
中,键为区域的 id
。regions
数组: id
。nodeMap
中查找父节点。children
数组中。id
等于 rootId
,将当前节点添加到 tree
数组中。tree
。三、工作流程 ▶️
regions
,每个区域对象包含 id
、name
和 pid
属性。convertToTree
函数将平铺的区域数据转换为树状结构。具体步骤如下: nodeMap
对象,用于存储每个节点的引用。regions
数组,将每个节点存储到 nodeMap
中,并初始化 children
属性。regions
数组,建立节点之间的父子关系,将子节点添加到父节点的 children
数组中。tree
数组中。getRegionDoms
函数将树状结构的数据转换为 HTML 列表,并将其添加到 output-container
元素中。具体步骤如下: ul
元素。li
元素,并将节点名称作为文本节点添加到 li
元素中。getRegionDoms
函数处理当前节点的子节点,并将结果添加到当前 li
元素中。li
元素添加到 ul
元素中。ul
元素添加到 output-container
元素中。通过以上步骤,实现了将平铺的省市区数据转换为树状结构,并以 HTML 列表的形式展示在网页上的功能。