前言
Element-UI
的 el-tree
组件是一个非常常用的组件,它可以用来显示和处理树形结构数据。然而,当处理大数据量时,重置为未勾选状态的操作可能会导致页面卡顿,这是因为该操作要进行大量的循环,而这些循环有些是不需要的。
分析与解决方法
el-tree
选中和取消勾选,会执行node.js
文件下的setChecked
函数。在setChecked
函数内会递归节点,设置勾选状态。
Element-UI
el-tree
源码文件路径:packeages\tree\src\model\node.js
函数执行逻辑如下:
graph TB
Q(setChecked) --> P(调用 getChildState 设置节点状态) --> O{handleDescendants} --> A{depp}
A{depp}-->|true| B(进入 for 循环递归) -.-> Q -.-> P -.-> F(reInitChecked 递归设置父级状态) -.-> P
A{depp}-->|false| I(reInitChecked 递归设置父级状态)
知道了大概的逻辑,就可以打断点
或者加console.log
来分析代码。为了能更好的在文章中展示,本次使用console.log
来分析代码。
使用三种不同的方法测试,重置勾选状态,包括在5000+
数据的情况下需要耗时多久。
- 使用官方推荐的
this.$refs.tree.setCheckedKeys([])
方法,设置所有选中节点为未勾选状态;
js
let nodes = this.$refs.tree.getCheckedNodes()
nodes.map(item => {
this.$refs.tree.setChecked(item)
})
一次性重置5000+
条数据耗时:
- 使用
getCheckedNodes()
获取选中节点,然后再使用setChecked
循环设置未勾选状态;
一次性重置5000+
条数据耗时:
- 使用
setChecked
,只设置选中节点的父级为未勾选状态;
js
this.$refs.tree.setChecked('4', false, true)
一次性重置5000+
条数据耗时:
总结::
- 测试
1.
全部节点循环了一遍,其中包括未勾选的节点; - 测试
2.
勾选了5个节点。取消勾选状态,循环调用getChildState
函数9次,加上循环调用this.$refs.tree.setChecked
的次数,总共循环了14次; - 测试
3.
只循环调用getChildState
函数3次;
以上,只计算了getChildState
函数调用次数,getChildState
函数内还有for循环,如果在大数据量的情况下,这么多次的循环和递归必然会造成卡顿。
尽量使用测试3.
的方法,只针对父级设置为未勾选状态
源码
html
<template>
<div>
<button @click="reset">重置</button>
<el-tree
ref="tree"
:data="data"
@check="check"
show-checkbox
node-key="id"
:default-expanded-keys="[]"
:props="defaultProps">
</el-tree>
</div>
</template>
<script>
export default {
data() {
return {
data: [{
id: 1,
label: '一级 1',
children: [{
id: 4,
label: '二级 1-1',
children: [{
id: 9,
label: '三级 1-1-1'
}, {
id: 10,
label: '三级 1-1-2'
}]
}, {
id: 99,
label: 'asasa'
},
{
id: 88,
label: 'vcvcvc'
}]
}, {
id: 2,
label: '一级 2',
children: [{
id: 5,
label: '二级 2-1'
}, {
id: 6,
label: '二级 2-2'
}]
}, {
id: 3,
label: '一级 3',
children: [{
id: 7,
label: '二级 3-1'
}, {
id: 8,
label: '二级 3-2'
}]
}],
defaultProps: {
children: 'children',
label: 'label'
},
setData: new Set()
};
},
created() {
for (let i = 0; i < 5000; i++) {
this.data[0].children.push({
id: i + 100,
label: i
})
}
},
methods: {
reset() {
console.time('耗时')
// this.$refs.tree.setCheckedKeys([])
let arr = Array.from(this.setData)
let node = null
for (let item of arr) {
node = this.$refs.tree.getNode(item)
if (!node.checked && !node.indeterminate) continue
this.$refs.tree.setChecked(item, false, true)
}
let nodes = this.$refs.tree.getCheckedNodes()
console.log(nodes)
// nodes.map(item => {
// this.$refs.tree.setChecked(item)
// })
console.timeEnd('耗时')
},
check(a) {
let node = this.$refs.tree.getNode(a.id)
this.setData.add(node.parent.key || node.key)
}
}
};
</script>
建议
Vue2
项目,建议大数据量的树形结构数据,使用vue-easy-tree
库,vue-easy-tree
很好的实现了虚拟列表树。
vue-easy-tree
使用el-tree
二次开发,所以可以很好的支持el-tree
的 API,可以很方便的替换原有的el-tree
。