Element UI Table tree component lazy loading problem fix

Last week, when using an elementUI table, I found that every time I click on the checkbox of the table, the page will be stuck for close to two seconds, so I used vue’s devtools to record where the operation time is consumed each time. It was found that the main reason is that although the elementUI table provides tree data and lazy loading functions, although the child nodes are not expanded, they have already been rendered.

Therefore, the elementUI table provides the function of lazy loading, but this lazy loading function is problematic, because the lazy loading function will only be called once, and no matter how the data is changed in the future, the sub-node will not change.

After analyzing the source code of vue and elementUI, I found the reason and fixed it appropriately. I will record it here.

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]);
}
}

The main purpose of the refreshLazyRows () function is to update’this. $refs.listResourceTableRef.store states.lazyTreeNodeMap ', which is the object that the child node listens to

As for why the existing mapping should be deleted first, it is because if the old one is not deleted, a new mapping will be added directly, and we will add a new node as the parent node, which will cause the node to be nested layer by layer, and It will cause elementUI to report a duplicate key error.