Element UI Table 树形组件懒加载问题修复

上周在使用一个elementUI的table是,发现每次点击table的checkbox的时候,页面都会卡顿接近两秒钟,于是就用vue的devtools去记录了下每次的操作时间消耗在哪里,发现主要是因为elementUI的table虽然提供了树形数据和懒加载的功能,虽然子级节点没有展开,但是已经渲染出来了。

所以elementUI的table提供了懒加载的功能,但是这个懒加载的功能是有问题的,因为懒加载函数只会调用一次,以后无论如何变化数据,子节点都不会发生改变。

经过对vue和elementUI的源码分析,找到原因后适当fix了一下,在这里记录一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<el-table
:data="parentResourceList"
border
class="list-resource-table-wrapper"
:height="tableHeight"
row-key="id"
lazy
:load="lazyLoadChildren"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
stripe
@selection-change="onSelectionChange"
:default-sort="{ prop: 'assetId' }"
v-loading="loading"
ref="listResourceTableRef"
>
</el-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
async loadResources() {
try {
this.loading = true;
let response = await resourceGateway.getAssets();
if (this.isSuccessResponse(response)) {
let rows = response.data;
let newResource = {};
this.childrenResourceMap = {};
rows.forEach((row) => {
if (newResource[row.assetId]) {
newResource[row.assetId].hasChildren = true;
(this.childrenResourceMap[row.assetId] || (this.childrenResourceMap[row.assetId] = [])).push(row);
} else {
newResource[row.assetId] = row;
}
});
let result = [];
_.keys(newResource).forEach((key) => {
const resource = newResource[key];
result.push(resource);
this.refreshLazyRows(resource);
});
this.parentResourceList = result;
} else {
this.$_error(this.$t('message.loadResourceFail'));
}
} catch (e) {
this.$_error(this.$t('message.loadResourceFail'));
} finally {
this.loading = false;
}
},
lazyLoadChildren(tree, treeNode, resolve) {
resolve(this.childrenResourceMap[tree.assetId]);
},
refreshLazyRows(resource) {
let lazyTreeNodeMap = this.$refs.listResourceTableRef.store.states.lazyTreeNodeMap;
if (this.childrenResourceMap[resource.assetId]) {
this.childrenResourceMap[resource.assetId].some((childResource) => {
if (_.get(lazyTreeNodeMap, childResource.id)) {
_.set(lazyTreeNodeMap, childResource.id, []);
return true;
}
});
}
if (_.get(lazyTreeNodeMap, resource.id)) {
_.set(lazyTreeNodeMap, resource.id, this.childrenResourceMap[resource.assetId]);
}
}

refreshLazyRows()函数的主要作用就是更新this.$refs.listResourceTableRef.store.states.lazyTreeNodeMap,这个值才是子级节点监听的对象

至于为什么先要把已有的映射删除,是因为如果不删除旧有的,就直接添加新的映射,同时我们又新增了一个节点当作父级节点,就会导致节点层层嵌套,并且会引起elementUI报key重复的错误。