上周在使用一个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重复的错误。