前言
Element-UI 的 el-tree 组件是一个非常常用的组件,它可以用来显示和处理树形结构数据。然而,当处理大数据量时,重置为未勾选状态的操作可能会导致页面卡顿,这是因为该操作要进行大量的循环,而这些循环有些是不需要的。
分析与解决方法
el-tree选中和取消勾选,会执行node.js文件下的setChecked函数。在setChecked函数内会递归节点,设置勾选状态。
Element-UIel-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。