Skip to content

Commit

Permalink
refactor(tree): setData improvement, and fix data update problems in …
Browse files Browse the repository at this point in the history
…Vue3 (#1603)

* refactor: 💡 tree 组件,完善数据更新 setData api,解决 vue3 的数据更新问题

* perf: ⚡️ tree 组件,改进清空节点的性能

* docs: add more documents

---------

Co-authored-by: sheepluo <sheepluo@tencent.com>
  • Loading branch information
TabSpace and sheepluo authored Oct 10, 2023
1 parent 185c6c1 commit 4e743c3
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 34 deletions.
7 changes: 7 additions & 0 deletions docs/web/api/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ spline: data

### 可过滤的树

- 过滤动作由 `filter` 属性的变更触发,故而不能只修改传递给 `filter` 属性内部的数据,应当在条件变化时变更 `filter` 属性。清空过滤条件时,通过设置 `filter=null` 来触发树组件展示状态还原。

- `allowFoldNodeOnFilter=false` 时,过滤状态下展开的路径节点无法被收起;`allowFoldNodeOnFilter=true` 时,过滤状态下展开的节点,允许点击收起,注意这会影响到树组件当前的展开状态数据。每次变更过滤条件时,会重设节点展开状态,将命中节点的路径节点展开。当清空过滤条件时,将会还原为设置过滤条件之前时的展开状态。

{{ filter }}

### 自定义
Expand Down Expand Up @@ -85,6 +89,9 @@ spline: data

#### 展开时加载节点

默认为点击加载数据。`valueMode` 默认为 `onlyLeaf`。选中父节点时,子节点由于未加载,无法更新和获取选中状态,导致无法更新父节点的状态。
所以使用延迟加载时,推荐 `valueMode` 设置为 `all` 或者 `parentFirst`

{{ lazy }}

<!-- ### 受控操作
Expand Down
2 changes: 1 addition & 1 deletion js/tree/tree-node-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ export class TreeNodeModel {
if (!isUndefined(dataValue)) _data.value = dataValue;
if (!isUndefined(dataLabel)) _data.label = dataLabel;
if (!isUndefined(dataDisabled)) _data.disable = dataDisabled;

Object.assign(node.data, _data);
Object.assign(node, _data);
node.update();
}
}

Expand Down
3 changes: 3 additions & 0 deletions js/tree/tree-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export class TreeNode {
// 节点隶属的树实例
public tree: TreeStore;

// 节点私有 id,不接受外部传入,确保唯一性
public [privateKey]: string;

// 节点 id ,唯一标志
public value: string;

Expand Down
70 changes: 38 additions & 32 deletions js/tree/tree-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import camelCase from 'lodash/camelCase';
import isPlainObject from 'lodash/isPlainObject';
import mitt from 'mitt';

import { TreeNode } from './tree-node';
import { TreeNode, privateKey } from './tree-node';
import {
TreeNodeValue,
TypeIdMap,
Expand All @@ -19,6 +19,7 @@ import {
TypeTreeFilterOptions,
TypeRelatedNodesOptions,
TypeTreeEventState,
TypeUpdatedMap,
} from './types';

function nextTick(fn: () => void): Promise<void> {
Expand Down Expand Up @@ -72,7 +73,7 @@ export class TreeStore {
public nodeMap: Map<TreeNodeValue, TreeNode>;

// 节点 私有 ID 映射
public privateMap: Map<string, TreeNode>;
public privateMap: Map<TreeNodeValue, TreeNode>;

// 配置选项
public config: TypeTreeStoreOptions;
Expand All @@ -81,7 +82,7 @@ export class TreeStore {
public activedMap: TypeIdMap;

// 数据被更新的节点集合
public updatedMap: TypeIdMap;
public updatedMap: TypeUpdatedMap;

// 选中节点集合
public checkedMap: TypeIdMap;
Expand Down Expand Up @@ -342,10 +343,6 @@ export class TreeStore {
* @return void
*/
public reload(list: TypeTreeNodeData[]): void {
this.expandedMap.clear();
this.checkedMap.clear();
this.activedMap.clear();
this.filterMap.clear();
this.removeAll();
this.append(list);
}
Expand Down Expand Up @@ -492,9 +489,17 @@ export class TreeStore {
* @return void
*/
public updated(node?: TreeNode): void {
if (node?.value) {
this.updatedMap.set(node.value, true);
const { updatedMap } = this;
if (node) {
// 传入节点,则为指定节点的更新
updatedMap.set(node[privateKey], 'changed');
} else {
// reflow 流程不传入节点,需要更新所有节点
this.getNodes().forEach((itemNode) => {
updatedMap.set(itemNode[privateKey], 'changed');
});
}

if (this.updateTick) return;
this.updateTick = nextTick(() => {
this.updateTick = null;
Expand All @@ -510,28 +515,23 @@ export class TreeStore {
// 以便于优化锁定检查算法
this.lockFilterPathNodes();

const updatedList = Array.from(this.updatedMap.keys());
if (updatedList.length > 0) {
// 统计需要更新状态的节点,派发更新事件
const updatedNodes = updatedList.map((value) => this.getNode(value));
this.emit('update', {
nodes: updatedNodes,
map: this.updatedMap,
});
} else if (this.shouldReflow) {
// 单纯的回流不需要更新节点状态
// 但需要触发更新事件
// 实际业务中,这个逻辑几乎无法触发,节点操作必然引发 update
// 这里代码仅仅用于边界兜底
this.emit('update', {
nodes: [],
map: this.updatedMap,
});
}
// stateId 用于单个节点状态监控
const stateId = `t${new Date().getTime()}`;
const updatedList = Array.from(updatedMap.keys());
const updatedNodes = updatedList.map((nodePrivateKey) => {
updatedMap.set(nodePrivateKey, stateId);
return this.privateMap.get(nodePrivateKey);
});

// 统计需要更新状态的节点,派发更新事件
this.emit('update', {
nodes: updatedNodes,
map: updatedMap,
});

// 每次回流检查完毕,还原检查状态
this.shouldReflow = false;
this.updatedMap.clear();
updatedMap.clear();
});
}

Expand Down Expand Up @@ -822,10 +822,16 @@ export class TreeStore {
* @return void
*/
public removeAll(): void {
const nodes = this.getNodes();
nodes.forEach((node) => {
node.remove();
});
this.expandedMap.clear();
this.checkedMap.clear();
this.activedMap.clear();
this.filterMap.clear();
this.nodeMap.clear();
this.privateMap.clear();
this.updatedMap.clear();
this.nodes = [];
this.children = [];
this.reflow();
}

/**
Expand Down
4 changes: 3 additions & 1 deletion js/tree/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,12 @@ export type TypeTreeNodeModel = TreeNodeModel<TypeTreeNodeData>

export type TypeTreeFilter = (node: TreeNodeModel<TypeTreeNodeData>) => boolean;

export type TypeUpdatedMap = Map<TreeNodeValue, string>;

export interface TypeTreeEventState {
node?: TreeNode;
nodes?: TreeNode[];
map?: TypeIdMap;
map?: TypeUpdatedMap;
data?: TypeTreeNodeData[];
}

Expand Down

0 comments on commit 4e743c3

Please sign in to comment.