iView
之Select
组件在性能上的优化
我们公司的组件库是基于iView
比较早的版本修改后的仅供公司内部使用的组件库,因此在使用的过程中就会遇到一些问题。接下来本文会对比Select
组件在性能上做出的优化。
Debounce
函数
我们先来回顾一下debounce
函数的使用场景:在前端页面我们会遇到一些频繁触发的事件;比如
鼠标的移动
mousemove
事件;
window
对象的resize
、scroll
事件;
keydown
,keyup
事件;
实际的使用过程中我们可能不需要每次触发事件的时候都去响应该事件,我们往往需要当用户停止操作多少ms
后去响应事件。这个时候我们就需要用到debounce
函数了。下面是一段debounce
函数
export function debounce(fn) {
let waiting;
return function() {
if (waiting) return;
waiting = true;
const context = this,
args = arguments;
const later = function() {
waiting = false;
fn.apply(context, args);
};
this.$nextTick(later);
};
}
这段代码的意思的意思是当DOM更新后去响应这个事件,并且DOM更新后只会执行一次
有了这些知识的准备我们就可以来谈谈使用这个组件遇到的性能问题了。
低版本iview
Select组件遇到的问题
在使用低版本的组件过程中,当数据量很大例如某个select
选择器里面有500条甚至更多的数据时,进行模糊搜索有可能卡死页面,关闭页面有延迟。
出现该问题的原因
在Select
组件的mounted
钩子中有三个监听,分别是组件添加,删除,和选中时监听事件。在监听事件中会去遍历Select
当中的所有子组件做出相应的改变。
//监听子组件的移除
this.$on('remove',() => {
if (!this.remote) {
this.modelToQuery();
this.$nextTick(() => this.broadcastQuery(''));
} else {
this.findChild((child) => {
child.updateSearchLabel(); // #1865
child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
});
}
this.slotChange();
this.updateOptions(true);
})
查找这个监听的通知对象发现正是Select
的子组件在销毁或者是创建时通知父组件做出相应的变化。
//组件销毁时通知父组件
beforeDestroy () {
this.dispatch('iSelect', 'remove');
this.$off('on-select-close', this.onSelectClose);
this.$off('on-query-change',this.onQueryChange);
}
那么问题就出在这里了,当有大量的子组件时,每一个组件移除,父组件需要遍历一次。这样就拖累性能。
解决办法
既然前面提到debounce
函数,想必大家应该想到要怎么解决了。使用debounce
函数能够解决这个问题,我们只需要所有子组件销毁时通知父组件一次就够了。引入debounce
函数后经本人测试基本上解决了卡顿以及卡死的问题,代码如下。
//select组件mounted函数当中去监听 append 、remove 事件
this.$on('append', this.debouncedAppendRemove());
this.$on('remove', this.debouncedAppendRemove());
//引入debounce函数
debouncedAppendRemove(){
return debounce(function(){
if (!this.remote) {
this.modelToQuery();
this.$nextTick(() => this.broadcastQuery(''));
} else {
this.findChild((child) => {
child.updateSearchLabel(); // #1865
child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
});
}
this.slotChange();
this.updateOptions(true);
});
}
其他细节
低版本中子组件的监听事件没有移除掉,高版本的有移除。
mounted () {
this.$on('on-select-close', this.onSelectClose);
this.$on('on-query-change',this.onQueryChange);
}
beforeDestroy () {
this.$off('on-select-close', this.onSelectClose);
this.$off('on-query-change',this.onQueryChange);
}