在近期的一个项目中,有一个树形菜单的功能,使用了 ElementUI 的 Tree 控件。页面上的效果如图所示:

在 Tree 控件中,需要在叶子节点被选中时请求后台的接口。Tree 控件有一个
check-change
属性,该属性是个方法,用来在节点被选中后调用。每次有节点被选中后,都会调用这个方法,当选择上级的节点时,下级的所有节点也会被选择,因此在批量选择时,可能会执行很多次 check-change
方法。如果在选择后,还需要进行类似接口请求等高耗操作,会非常影响浏览器性能。在我的代码中,每个叶子节点被选中时,就会向后台发起一个请求获取节点详情,如果是批量选择,就会发出大量的 HTTP 请求,浏览器顿时卡住了,半天才恢复过来,即使是强悍的 Chrome 也难逃厄运。
下面是我代码中封装的组件:
<template>
<div
class="tree-box"
:id="treeId"
>
<el-tree
:props="props"
:load="load"
lazy
@check-change="handleCheckChange"
@node-click="handleNodeClick"
ref="tree"
:show-checkbox="checkable"
:highlight-current="true"
>
...
组件的 handleCheckChange
方法定义如下:
handleCheckChange(data){
if(!data.leaf) return;
if(choosen){
this.check(data);
}else{
this.unCheck(data);
}
},
性能问题的产生不在于 handleCheckChange
方法,而在于选中/取消选中后的回调函数 check
和 unCheck
(这两个函数是通过 props
传入的)。在 check
和 unCheck
函数中,每有一次点击,就会执行一次高耗操作,导致界面卡顿。我的解决方案是为 check
和 unCheck
应用防抖机制。
下面是 check
函数的代码:
async check(data){
// 加锁,禁止添加已存在的元素
const lockFlag = this.checkedQueue.find(...);
if(lockFlag) return;
clearTimeout(this.check.timer);
this.checkedQueue.push(data)
this.check.timer = setTimeout(async () => {
// 根据 checkedQueue 进行批量处理
const res = await httpRequest()
// 处理结果集
...
// 清空 checkedQueue
this.checkedQueue = []
},20)
}
上面是一个很经典的防抖代码,通过对 check
函数设置防抖,用来批量“收集”被选择的子节点信息,然后后端提供一个批量请求的接口,执行请求即可。
通过这个例子,我明白了函数节流和函数防抖不只可以应用于事件处理中,函数防抖和函数节流是针对函数频繁调用时引起性能问题的通用解决方案。针对批量选择,函数防抖可以用来“检测变化”,即一段时间后选择的内容没有发生变化,就可以针对这一批次的选择进行处理了,防抖函数的特性非常适合做这样的事情,且效果比检测数组长度变化的方案或者定时器方案要好,防抖函数天生适合用来做批量选择时的“变更检测”。对于其他函数调用中的类似需求,也可以使用防抖或者节流的思想来帮助我们解决问题。
完。
网友评论