最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何优雅的让vue长列表快速加载

    正文概述 掘金(墓中无人)   2021-03-25   900

    背景

    有个长列表渲染的需求,本来用vue-virtual-scroll-list的。但是每个节点的高度不一样,用着有点问题。如果也有相应的需求可以参考下我的方案。欢迎大家交流!

    vue-long-list-load

    满足特殊条件的的长列表加载。 列表内各个节点高度不一,各个节点可以进行修改,定位到指定位置指定节点。 www.npmjs.com/package/vue…

    主要内容

    1. vue-long-list-load 与 vue-virtual-scroll-list 对比
    2. vue-long-list-load实现思路
    3. 基本代码
    4. 插件使用方式
    5. 插件参数说明

    一、组件对比

    vue-long-list-load ,vue-virtual-scroll-list两个插件各有优缺点,当我们在选择插件的时候要选择最适合应用场景的插件。下面是两个插件的基本功能对比。vue-long-list-load 主要特点是适用于各个节点尺寸不统一的场景,vue-virtual-scroll-list更适用于高度统一节点以列表长度以w计的这种列表。

    组件vue-long-list-loadvue-virtual-scroll-list
    npm地址www.npmjs.com/package/vue…www.npmjs.com/package/vue…核心先空dom占位,显示区域内组件挂载显示计算当前显示区域内要显示的组件挂载横向支持支持列表内高度一致支持支持列表内高度不一致支持支持不好滚动到指定组件支持高度不一致时计算不准确滚动事件支持支持组件高度改变事件支持支持隐藏组件支持不支持

    二、实现思路

    主要思想就是通过虚拟dom\color{red}{虚拟dom}虚拟dom占位各个节点,根据可显示视口的变化来展示该展示的节点。影响可显示视口的因素的监听,页面整体宽高变化、节点高度变化、页面的滚动定位到某一个节点等都可能会影响到视口的变化。当视口发生变化后计算可显示的节点,将可显示的节点挂载上节点的组件,不在视口内的节点销毁组件紧保留一个空的div。下图为实现思路的流程图。 如何优雅的让vue长列表快速加载

    三、关键方法源码分析

    主入口html结构如下,vfor\color{red}{v-for}v−for 展示长列表长度的的节点,并且通过:style 来设置一个最小高度\color{red}{最小高度}最小高度,设置最小高度的原因是这个高度值可能不准确,当真正组件渲染完之后计算出最准确的高度,如果直接height的话可能会使节点内的组件展示不全。同时每个节点设置唯一id(scrollItem_ 唯一标识),在根据数据获取dom信息时候使用。节点组件定义了唯一的class (long-item- 唯一标识),主要用来挂载真实列表组件,同时监听组件的高度变化。showList[index]来控制节点是否时候显示的唯一标识。

        <!--html代码-->
        <template>
            <div  
                :class="setVirtualClass(item,index)"
                :style="{'min-height': (item.height>=0?item.height:height) + 'px'}"
                :key="item[dataKey]"
                :id="'scrollItem_' + item[dataKey]"
                v-for="(item,index) in dataList"  
                >
                <long-item 
                    v-if="showList[index]"
                    :dataKey="dataKey" 
                    :item="item"
                    :boxHeight="item.height||0"
                    :direction="direction"
                    :heightChange="heightChange"
                    :extendCcomments="extendCcomments"> 
                </long-item>
            </div>
        </template>
    

    当showList[index]为true的时候,对应的节点显示。long-item 在mounted生命周期时,回调extendCcomments。通过Vue.extendProfile\color{red}{Vue.extend Profile}Vue.extendProfile挂载到对应的dom上。componentProps是节点组件传过来的一些参数,在挂载的时候全部挂载上。

     <!--挂载组件-->
        extendCcomments(item){
            this.componentProps.item=item
            var Profile = Vue.extend(this.dataComponent);
                    // 创建 Profile 实例,并挂载到一个元素上
                    new Profile({
                        propsData:this.componentProps
                    }
                ).$mount('.long-item-'+item[this.dataKey]);
        }
    

    通过elementresizedetector\color{red}{element-resize-detector}element−resize−detector来监听dom尺寸的变化,每个节点的宽高发生变化的时候,并且与原来的尺寸不一样回调heightChange方法,进行尺寸的更新及显示节点的操作计算。

      <!--每个节点尺寸发生变化-->
        this.$nextTick(()=> {
            this.$DomListener.listenTo(document.getElementById('long-item-'+this.item[this.dataKey]), (element)=>{
                if(this.boxHeight != element[this.directionConfig.width]){
                    this.heightChange(this.item, element[this.directionConfig.width])
                } 
            })
        });
    

    获取可显示的视口区域的方法,页面滚动和尺寸变化都会调用到这个方法,所以这个方法在调用的时候做防抖处理300ms内有连续调用只会执行最后一次调用,要不然会频繁计算影响性能。可显示视口区域计算方式是当前视口 及 前后两个视口总共三个视口的尺寸。这样在小的滚动范围内会有较好的体验。

        getShowLimit(startTop) {
            const scrollTop = startTop || this.scrollWrap[this.directionConfig.scrollTo] || 0; // 滚动距离
            this.viewClientHeight =  this.scrollWrap[this.directionConfig.width]; // 可视区域高度
            this.scrollTop = scrollTop
            this.showStart = scrollTop - this.viewClientHeight
            this.showEnd = scrollTop + 2*this.viewClientHeight
            if(this.setTopTimer){
                clearTimeout(this.setTopTimer)
            } 
            this.setTopTimer = setTimeout(() => {
                this.setItemTopheight()
            }, 300);
        },
    

    根据高度或者宽度来计算节点是否显示,因为这个计算量比较大避免而且这个方法与其他方法没有什么关联,直接单独开一个独立线程\color{red}{独立线程}独立线程进行计算。通过引入simplewebworker\color{red}{simple-web-worker}simple−web−worker这个插件单独开一个线程进行计算显示节点。计算方法主要有三点:当前节点的开头在显示视口内、当前节点的结尾在显示视口内、当前节点开头和结尾都不在显示视口内。分为这三种情况,只要满足一种情况,则该节点就显示在显示视口中。

        // 根据高度计算节点是否显示
        setItemTopheight(){
            let stsartId = this.dataList[0]&&this.dataList[0][this.dataKey]
            let startDom = stsartId && document.getElementById('scrollItem_'+stsartId)
            let startTop = startDom ? startDom[this.directionConfig.offset] : 0
            this.worker = this.$worker.run((dataList,showStart,showEnd, startTop,hideIds,dataKey,height) =>{
                let topHeight = startTop; // 题目顶部距离顶部距离
                let bottomHeight = 0; // 题目底部距离顶部距离
                let showList = []
                for(let i=0,len=dataList.length;i<len;i++){
                    let item = dataList[i]
                    if(hideIds.indexOf(item[dataKey]) != -1){
                        showList[i] = false;
                        continue;
                    }
                    bottomHeight = topHeight + (item.height>=0?item.height:height)
                    // 判断 1.题目顶部在显示范围内 2.题目底部在显示范围内  3.题目顶部和底部都不在显示范围内 
                    if((topHeight>=showStart && topHeight<=showEnd)||
                        (bottomHeight>=showStart && bottomHeight<=showEnd)||
                        (topHeight<showStart && bottomHeight>showEnd) ){
                        showList[i] = true}
                     else{
                        showList[i] = false
                    }  
                    topHeight += ((item.height>=0?item.height:height));
                }
                return showList
            }, [this.dataList, this.showStart, this.showEnd, startTop, this.hideIds,this.dataKey,this.height])
            .then(res => {
                this.showList = res
            })
            this.worker = null
        },
    

    四、使用方式

    1. 安装vue-long-list-load:
    npm install vue-long-list-load --save
    
    1. 项目内调用
    <long-list 
      ref="vueLongList"
      dataKey='id' 
      scrollWrapId="manWrap"
      :dataList="dataList" 
      :dataComponent="dataComponent" 
      :componentProps="componentProps"
      height=100
      > 
    </long-list>
    
    

    五、参数说明

    参数说明类型必填可选值默认值
    scrollWrapId列表滚动容器idstringtruedataKey节点数据内唯一键值StringtruedataList列表数据Arraytrue具体见下方说明dataComponent自定义的节点组件trueref调用组件内部方法stringfalsedirection滚动方向Numberfalse0:纵向,1横向0hideIds列表中需要隐藏的节点Arrayfalse具体见下方说明[]height节点高度Numberfalse100componentProps节点组件要传递的参数Objectfalse{}scroll滚动区域内滚动回调方法Functionfalse-resized某个节点宽高发生变化回调方法Functionfalse具体见下方说明

    部分参数说明

        &lt;--假设 dataKey=id-->
        
        &lt;--列表中需要隐藏的节点-->
        hideIds:[1, 2]
        &lt;--列表数据  dataList 内 height 为 **Number**。-->
        dataList:[
            {id:1,height:100},
            {id:2,height:200},
            {id:3,height:300},
            {id:4,height:300},
            {id:5,height:300}
        ]
        
       &lt;--节点高度-->
       height:100
       &lt;--如果dataList的数据内有height值 不需要设置这个height-->
       &lt;--如果dataList 和 height 都不传递的话,默认为100 可能滚动略有卡顿;-->
       &lt;--建议在每个高度都不相同的时候通过dataList传递,都相同时候通过height传递-->
       
       &lt;--某个节点宽高发生变化回调方法  返回参数为id 与高度-->
       resized(id, height){  }
    

    总结

    项目中实践数据,基本每个节点至少500个dom节点,平均也在800个dom节点以上,用vue-long-list-load 不在渲染区域内的题目只会渲染2个dom节点。按正常800左右个dom节点的题目计算 一般渲染区域渲染的题目在9个左右,如果是n道题的问卷 ,每次加载 dom操作都减少(n-9)x(800-2)个dom的渲染,如果1000个节点\color{red}{1000个节点}1000个节点的问卷每次加载和操作的时候相当于减少了726180\color{red}{726180}726180个dom节点的渲染。首次渲染还有修改题目后的重绘都大大减少了dom的渲染。 此方案已经在项目中实践一段时间,用户反馈很好。如果大家也有类似的场景需求,欢迎大家使用!交流!


    起源地下载网 » 如何优雅的让vue长列表快速加载

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元