最近有项目需要加载大型树数据,数据大概3W条
后端使用C# NET6
前端使用Vue3 elementuiplus 虚拟tree =》解决大型树数据加载
遇到的问题是后端在检索数据时,要返回匹配数据的完整树目录
1.因为单条数据没有存放完整路径,需要通过父级ID逐级查找组装,导致性能急剧下降
2.数据包数据20M左右,前端下载受带宽影响也变慢
下面来说问题1
采用BOM树缓存,筛选时使用Queue来处理遍历数据加载符合条件的树数据如下
/// <summary>
/// 过滤与重建树数据
/// </summary>
/// <param name="tree"></param>
/// <param name="predicate"></param>
/// <param name="qualifiedNodes"></param>
/// <returns></returns>
public static List<BOM_DetailTree> FilterAndRebuildTreeOptimized(List<BOM_DetailTree> tree, Func<BOM_DetailTree, bool> predicate, HashSet<long> qualifiedNodes)
{
var result = new List<BOM_DetailTree>();
// 使用队列来处理树的广度优先遍历
var queue = new Queue<(BOM_DetailTree Node, List<BOM_DetailTree> Result)>();
queue.Enqueue((tree.FirstOrDefault(n => n.ParentID == null || n.ParentID == 0), result));
//while (stack.Count > 0)
while (queue.Count > 0)
{
var (currentNode, currentResult) = queue.Dequeue();
// 如果当前节点满足条件或其子节点中有满足条件的,添加到结果树
if (predicate(currentNode) || HasQualifiedChild(currentNode, predicate))
{
var newNode = new BOM_DetailTree
{
Id = currentNode.Id,
ProjectCode = currentNode.ProjectCode,
MaterialCode = currentNode.MaterialCode,
MaterialName = currentNode.MaterialName,
ParentID = currentNode.ParentID,
ParentCode = currentNode.ParentCode,
MaterialCategory = currentNode.MaterialCategory,
QtyPerPiece = currentNode.QtyPerPiece,
UnAllotNum = currentNode.UnAllotNum,
ManualTotalQuantity = currentNode.ManualTotalQuantity,
IsPart = currentNode.IsPart,
IsStandard = currentNode.IsStandard,
PublishedStatus = currentNode.PublishedStatus,
IsUrgency = currentNode.IsUrgency,
NodeLevel = currentNode.NodeLevel,
IsSupplement = currentNode.IsSupplement,
AlterationStatusLable = currentNode.AlterationStatusLable,
IsShowTooltip = currentNode.IsShowTooltip,
IsAddBOM = currentNode.IsAddBOM,
};
currentResult.Add(newNode);
// 添加当前节点的子节点到栈中
if (currentNode.Children != null)
{
foreach (var child in currentNode.Children)
{
// stack.Push((child, newNode.Children ?? new List<BOM_DetailTree>()));
queue.Enqueue((child, newNode.Children ?? new List<BOM_DetailTree>()));
}
}
}
}
return result;
}
/// <summary>
/// 判断节点与子节点是否有匹配
/// </summary>
/// <param name="node"></param>
/// <param name="filterPredicate"></param>
/// <returns></returns>
private static bool HasQualifiedChild(BOM_DetailTree node, Func<BOM_DetailTree, bool> filterPredicate)
{
if (filterPredicate(node))
{
return true;
}
if (node.Children != null)
{
foreach (var child in node.Children)
{
if (HasQualifiedChild(child, filterPredicate))
{
return true;
}
}
}
return false;
}
使用时
Func<BOM_DetailTree, bool> filterPredicate = node =>
node.PublishedStatus == 1;
var qualifiedNodes = new HashSet<long>();
var newTree= FilterAndRebuildTreeOptimized(cacheTree, filterPredicate, qualifiedNodes);
过滤效率毫秒级。
第二个问题
Startup添加响应数据压缩
// 添加响应压缩服务
services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<GzipCompressionProvider>();
options.Providers.Add<BrotliCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/json" });
});
app.UseResponseCompression();
优化后即时数据大的情况基本在2S以内响应结果,满足使用