如何基于RXJS实现树节点递归全展开且不修改源数据
解决方案
核心思路是调整递归函数的返回值为单个完整节点的Observable,全程通过浅拷贝组装新节点,完全不修改原始数据,同时简化了管道逻辑。
完整实现代码
类型定义(补充)
interface Node { id: number; parent: boolean; children?: Node[]; // 可扩展其他原有属性 } // 原有服务请求方法保持不变 declare function getChildrenFromServer(nodeId: number): Observable<Node[]>;
核心递归加载方法
/** * 递归加载单个节点的完整层级结构,返回新的节点对象,不修改输入的原始节点 */ function loadFullNode(originalNode: Node): Observable<Node> { // 非父节点直接返回浅拷贝,无需请求子节点 if (!originalNode.parent) { return of({ ...originalNode }); } return getChildrenFromServer(originalNode.id).pipe( // 并行递归加载所有子节点的完整结构 mergeMap(children => forkJoin( (children || []).map(child => loadFullNode(child)) )), // 组装新节点,无任何副作用 map(fullChildren => ({ ...originalNode, ...(fullChildren.length ? { children: fullChildren } : {}) })) ); }
调用方式
// 原始根节点,全程不会被修改 const rootNodes = [{ id: 0, parent: true }, { id: 1000, parent: true }]; // 加载整棵树 forkJoin(rootNodes.map(node => loadFullNode(node))) .subscribe(fullTree => { // 输出完整树结构 console.log(fullTree); // 验证原始数据未被修改 console.log(rootNodes); });
优化点说明
- 无副作用:所有节点操作都基于浅拷贝生成新对象,完全不改动原始输入数据
- 逻辑简化:移除了原实现中
filter、tap等副作用操作,递归逻辑和数据组装逻辑拆分清晰 - 加载效率不变:同级节点的请求保持并行发起,和原实现加载效率一致
- 扩展性更好:可直接在
loadFullNode中添加单个节点的加载错误兜底、超时等逻辑,不影响全局
内容的提问来源于stack exchange,提问作者Stavm
相关产品推荐
相关产品推荐

