iView之Select组件的优化

iViewSelect组件在性能上的优化

    iViewElemeUI是目前用的比较多的UI组件库了,我们项目组使用的就是iView;不过,需要吐槽的是,我们的组件库是基于iView 1.x 版本修改后的仅供公司内部使用的组件库(完全不知道这样做的意义何在,为啥不直接用开源项目),因此在使用的过程中就会遇到一些问题。接下来本文会对比Select组件在性能上做出的优化。

Debounce函数

    我们先来回顾一下debounce函数的使用场景:在前端页面我们会遇到一些频繁触发的事件;比如

鼠标的移动 mousemove事件;

window对象的resizescroll事件;

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更新后只会执行一次
有了这些知识的准备我们就可以来谈谈使用这个组件遇到的性能问题了。

v1.x版本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);
    });
}

其他细节

    1.x版本中子组件的监听事件没有顺便移除掉,2.x版本优化了。

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);
}

猜你喜欢

转载自www.cnblogs.com/baimeishaoxia/p/12808259.html