TypeScript实现jQuery递归树形结构生成性能优化求助
优化Tree节点渲染性能的解决方案
问题分析
从你的代码来看,处理413个3层级节点耗时超2秒,核心瓶颈在于频繁的DOM操作和重复编译Handlebars模板:
- 每次递归都直接将新创建的DOM节点append到页面,触发多次重排重绘;
- 每个节点渲染时都重新编译Handlebars模板,这是不必要的性能开销;
- 大量使用JQuery的
$()创建单个元素,多次append操作累积了额外的性能成本。
优化方案
下面是针对性的优化代码,核心思路是:离线构建HTML内容,一次性插入DOM;预编译Handlebars模板,避免重复编译;减少JQuery的频繁调用。
优化后的TypeScript代码
// 预编译Handlebars模板,只执行一次 let compiledTemplate: HandlebarsTemplateDelegate | null = null; static createParentNodes($tree: JQuery, $nodeContainer: JQuery, nodes: any, settings: any) { // 预编译模板,避免重复编译 if (!compiledTemplate) { let textTemplate = settings?.textTemplate || $tree.data("textTemplate") || ""; compiledTemplate = Handlebars.compile(textTemplate); } // 使用DocumentFragment离线构建DOM,避免频繁重排 const fragment = document.createDocumentFragment(); const $ul = $('<ul></ul>')[0]; nodes.forEach((nodeData: any) => { const liHtml = gmTree.buildLiNodeHtml($tree, nodeData, settings, compiledTemplate); $ul.innerHTML += liHtml; }); fragment.appendChild($ul); $nodeContainer.append(fragment); } static buildLiNodeHtml($tree: JQuery, data: any, settings: any, template: HandlebarsTemplateDelegate): string { const treeId = $tree.attr("id") || ""; const checkboxId = `chk_${data.id}`; const finalData = data.data || data; const displayContent = template(finalData); // 构建基础Li的HTML字符串 let liHtml = `<li class="treeLi" data-id="${data.id}" data-nodeData='${JSON.stringify(data)}'`; if (settings?.parentId) { liHtml += ` data-parentId="${settings.parentId}"`; } liHtml += `>`; // 构建节点内容区域 liHtml += `<div class="tree-li-data">`; // 处理展开器和复选框的显示状态 const expanderClass = data.children?.length > 0 ? "" : "hidden"; liHtml += `<span class="${expanderClass}" data-role="tree-expander"><i class="material-icons">add</i></span>`; const checkboxClass = data.children?.length > 0 ? "hidden" : ""; liHtml += `<span class="${checkboxClass}" data-role="tree-checkbox"><input type="checkbox" id="${checkboxId}" class="chk-col-blue"/><label for="${checkboxId}"></label></span>`; liHtml += `<span data-role="tree-display" id="${treeId}Dis${data.id}">${displayContent}</span>`; liHtml += `</div>`; // 递归构建子节点的HTML if (data.children?.length > 0) { liHtml += `<ul>`; data.children.forEach((child: any) => { liHtml += gmTree.buildLiNodeHtml($tree, child, { parentId: data.id }, template); }); liHtml += `</ul>`; } liHtml += `</li>`; return liHtml; } // 插入DOM后再统一绑定数据和触发事件 static initTreeNodes($tree: JQuery) { $tree.find('.treeLi').each(function() { const $li = $(this); const nodeData = JSON.parse($li.data('nodeData') as string); const $parent = $li.closest('li'); $li.data("parent", $parent); $li.data("objectData", nodeData); $li.trigger(ON_NODE_CREATED, { data: nodeData.data || nodeData }); }); }
优化点说明
- 预编译Handlebars模板:模板只编译一次,所有节点共享编译后的函数,避免了重复编译的开销;
- 离线构建HTML字符串:通过字符串拼接生成所有节点的HTML,最后一次性插入到DocumentFragment,再添加到DOM,大幅减少重排重绘次数;
- 减少JQuery调用:避免了每次创建单个JQuery元素的开销,改用原生DOM的DocumentFragment和innerHTML来批量处理;
- 延迟数据绑定和事件触发:所有节点插入DOM后再统一绑定数据和触发
ON_NODE_CREATED事件,减少零散操作的性能消耗。
使用方式
调用时先构建整个树的HTML,再初始化节点数据和事件:
// 假设获取到nodes数据源后 gmTree.createParentNodes($tree, $nodeContainer, nodes, settings); gmTree.initTreeNodes($tree);
这样处理后,400+节点的渲染耗时应该能降到几百毫秒以内,显著提升性能。
内容的提问来源于stack exchange,提问作者Bahman Shafiei
相关产品推荐
相关产品推荐

