虚拟列表实现

实现非完整渲染的两种思路
1 .懒渲染:常见的无限滚动,每次只渲染一部分,剩余部分滚动到可见区域,就可以在渲染另一部分
2 .可视区域渲染饭:只渲染可见部分,不可见部分不渲染

一般的实践

1 .初始的数据是完整的,不会动态增加,不会出现从一条没有到不断增多的情况
2 .升级的版本就是每一项高度不确定的情况

实际的项目

1 .数据会从0开始增加,而且还会不端的增加,每增加一次数据,都需要滚动到最新的内容
2 .那其实就是会出现两个控制数据展示的情况,一个是鼠标滚动,切换显示的数据
3 .一个就是新增数据的时候,需要滚动到最新。滚动其实分为两部分

1 .数据的滚动:transform实现
2 .滚动条的滚动:

界面滚动到某部分的实现

1 .锚点快速定位

1 .本来的思路就是主页面滚动的部分也让滚动条使用scrollBy滚动响应的位置即可。
2 .但是算的太麻烦了,还不如在元素的最下面加一个看不见的锚点,每次都直接让他滚到最下方锚点的位置
3 .$('html, body').animate({scrollTop: $('#box').offset().top}, 1000)
4 .实测锚点不行,还是需要scrollBy这个方法来操作

2 .js获取并修改scrollTop:

1 .返回顶部
2 .document.documentElement.scrollTop=0

3 .scrollBy():每次以固定的步长进行移动
4 .scrollTo():直接移动到固定的位置

总结

1 .scrollLeft/scrollTop这两个属性只能作为元素上,在window对象上没有效果。而pageXOffset/pageYOffset只能作用于window对象上,在元素上没有效果。而scrollTo和scrollBy不仅可以作用于window对象上,还可以作用于元素上。实现的调用的统一。
2 .scrollLeft/scrollTop和pageXOffset/pageYOffset控制滚动定位,想要定位平滑,只能借助于CSS scroll-behavior属性,JS这块设置无力。但是scrollTo和scrollBy在比较方便,直接有API参数支持。
3 .兼容性

if (!window.scrollTo) {
    window.scrollTo = function (x, y) {
        window.pageXOffset = x;
        window.pageYOffset = y;
    };
}
if (!window.scrollBy) {
    window.scrollBy = function (x, y) {
        window.pageXOffset += x;
        window.pageYOffset += y;
    };
}
if (!document.body.scrollTo) {
    Element.prototype.scrollTo = function (x, y) {
        this.scrollLeft = x;
        this.scrollTop = y;
    };
}
if (!document.body.scrollBy) {
    Element.prototype.scrollBy = function (x, y) {
        this.scrollLeft += x;
        this.scrollTop += y;
    };
}

实现原理

1 .div结构

<div  ref="scroll_wp" @scroll="handleScroll" class="List_wp">
//整个父级div:用来显示里面的所有列表
//监听scroll事件,通过scroll事件来让下面的页面滚动
//他的height,width就是展示界面的大小

       <div class="List_scroll" :style="computedScroll" ref="list_scroll"></div>
//注意computedScroll,所以这个用来撑起来滚动条,这里决定是否可以滚到对应的部分,所以这里的值一定要是所有元素的height的和,每有新的元素,高度都要加到这个里面

       <div class="List_view" ref="list_view">
         <LineLi  v-for="(c,index) in visiableData" 
                    :msg="c.msg"
                    v-bind:from="c.from"
                    :index="index"
                    :key="c.index"
                    /> 
        </div>   
//这是具体的每一条的数据
    </div>

mounted事件

1 .当dom结构发生复用的时候,并不能检测到传入props的变化,需要使用别的生命周期函数
2 .问题

1 .想要的是每新加一个数据,都要新算他的高度,凑出总的高度,但是mounted事件会导致后面的不会触发这个事件,应为他是复用的,旧的数据是不触发事件的。
2 .update()事件:这个是会导致其实每一个都是走的复用逻辑,前一个数据使用的后一个数据的dom结构,会导致每增加一个元素,计算一遍之前的高度。
3 .加了key之后发现虽然不会总算了,但是发现每次滑动也还会触发muunted事件
4 .我发现自己实现组件的方法还是有问题
5 .

最大不同

1 .别人的例子都是,初始给出的数据是固定的,不会增加的
2 .在这个场景里面,由于是websocket应用,会不停的增加数据。所以初始的数据是无时无刻都在变的。而且还要求最新的数据要一
直在视野里面,就是新增数据要滚动
3 .夭折版本:实现了固定高度的虚拟列表

<template>
    <div  ref="scroll_wp" @scroll="handleScroll" class="List_wp">
       <div class="List_scroll" :style="computedScroll" ref="list_scroll"></div>
       <div class="List_view" ref="list_view">
         <LineLi  v-for="(c,index) in visiableData" 
                    :msg="c.msg"
                    v-bind:from="c.from"
                    :key="c.msg"
                    /> 
        </div>   
    </div>
</template>
<script>
import LineLi from './line'
export default {
    props:{
        data:{
            type:Array,
            default:function(){
                return []
            }
        },
        // 需要展示的数据

        lineHeight:{
            type:Number,
            default:35,
        },
        // 单行文本的高度
    },
    data:function(){
        return {
            visiableData:[],
            count:null,
            start:0,
            scrollOffset:0,
            end:null,
            wrapperHeight:0,

            totalHeight:0,
            heights:[],
        }
    },
    components:{
        LineLi,
    },
    computed:{
        computedScroll(){
            let height=(this.data.length+2)*this.lineHeight+"px"
            return {
                "height":height,
            }
        },
    },
    methods:{
        handleScroll(){
            this.scrollOffset=this.$refs.scroll_wp.scrollTop
        },
        scrollTop(offset){
            this.$refs.list_view.style.webkitTransform=`
                translate3d(0,${offset}px,0)
            `
        },
       
        
    },
    mounted(){
       // 一开始这里没值。。
            this.wrapperHeight=this.$refs.scroll_wp.getBoundingClientRect().height
            this.count=Math.floor(this.wrapperHeight/this.lineHeight)
            
            // this.end=this.start+this.count
            // this.visiableData=this.data.slice(this.start,this.end)
            // 计算出最小高度的时候需要展示的数据的数目。这样会出现渲染数据多的情况,而不会出现展示不足的情况
    },
    created(){  
        function throttle(func,delay){
            let lastTime=null

            return function(){
                let nowTime=new Date()
                if(nowTime-lastTime>delay||!lastTime){
                    func();
                    lastTime=nowTime
                }else{
                    return 
                    console.log('我正在控制这个函数的触发次数')
                }
            }
        }
        // this.newScroll=throttle(this.handleScroll,300)
    },
    watch:{
        data(n,o){
            let nlen=n.length
            if(nlen<=this.count){
                // 这里如果是等于就会少一个
                this.visiableData=n
            }else{
                // console.log(nlen,"change")
                // this.scrollOffset+=35

                // 这俩会互相影响
                // 还需要一个让里面的页面滚动的部分,现在只是走了让他偏移的大小,但是实际上是没有滚动的
               

                this.$refs.scroll_wp.scrollBy(0,35)
                // console.log(this.$refs.newLine.getBoundingClientRect().top)
            }
            
        },
        
        scrollOffset(n,o){
            this.scrollTop(n) 
            

            this.start=Math.floor(n/35)
            this.end=this.start+this.count 
            // console.log(this.start)  
            // console.log(this.end-this.start)
            this.visiableData=this.data.slice(this.start,this.end)          
        }
    },
    
}
</script>

<style lang="less" src="./list.less">

</style>

<template>
    <div class="hp_line" :title="time" ref="hp_line">
        <div class="hp_line_cell">
            <div class="hp_line_svg">
                    <svg class="icon" aria-hidden="true" v-if="from==1">
                        <use xlink:href="#icon-yunfuwuqi-copy-copy"></use>
                    </svg>
                    <svg class="icon" aria-hidden="true" v-else-if="from==2">
                        <use xlink:href="#icon-youxi1-copy"></use>
                    </svg>
                    <svg class="icon" aria-hidden="true" v-else-if="from==3">
                        <use xlink:href="#icon-gongjuxiang-copy"></use>
                    </svg>
                    <svg class="icon" aria-hidden="true" v-else>
                        <use xlink:href="#icon-gongjuxiang-copy"></use>
                    </svg>
            </div>
            <div class="hp_line_msg">
               {{msg}}
            </div>
        </div>
    </div>
</template>
<script>
export default {
    props:{
        from:{
            type:Number,
            default:0
        },
        msg:{
            type:String,
            default:'空'
        },
        time:{
            type:String,
            default:'123123'
        },
        isError:{
            type:Boolean,
            default:false
        },
        index:{
            type:Number,
            default:30
        }
    },
    data:function(){
        return {
            height:35
        }
    },
    mounted(){
        // this.$parent.totalHeight+=this.$refs.hp_line.getBoundingClientRect().height
        // this.$parent.heights.push(this.$parent.totalHeight)
        // console.log(this.$parent.totalHeight)
        // console.log(this.$parent.heights)
    },
    beforeUpdate(){
         // 计算一下自己的高度
        // this.$parent.totalHeight+=this.$refs.hp_line.getBoundingClientRect().height
        // this.$parent.heights.push(this.$parent.totalHeight)
        // console.log(this.$parent.totalHeight)
        // console.log(this.$parent.heights)
        // 
    }
}
</script>
<style lang="less" src="./line.less">

</style>

6216618401001324664

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容