vue 实现触底加载

188 阅读1分钟

基础环境

  • Vue 2.6
  • ant-design-vue
  • lodash

具体实现

最外层区域

  1. 设置外层div固定高度height=300px;
  2. 监听滚动事件handleScroll,handleScroll函数进行节流处理(防抖比作等电梯,节流比作过红绿灯;防抖执行最后一个,节流执行第一个
  3. 垂直方向可滚动
<template>
    <a-spin tip="loading" :spinning="loading">
        <div class="outer-box" @scroll="handleScroll" :style="{height: height.toString + 'px'}">
        <div>
    </a-spin>
</template>

<script>
export default {
    data() {
        return {
            height: 300,
            page: 1
        }
    }
}
</script>

<style scoped> 
.outer-box {
    border: 1px solid #ddd;
    overflow-y: auto;
}
</style>

中间区域

  • 增加一个中间区域,高度设置为总的行数(totalNum=1000) * 行高(itemHeight=30px),该区域的作用是解决“滚动条抖动”的问题
<template>
    <a-spin tip="loading" :spinning="loading">
        <div class="outer-box" @scroll="handleScroll" :style="{height: height.toString + 'px'}">
            <!--中间区域-->
            <div :style="height: (itemHeight * item.totalNum).toString() + 'px'">
            </div>
        <div>
    </a-spin>
</template>

<script>
export default {
    data() {
        return {
            height: 300,
            itemHeight: 30,
            totalNum: 0,
            data: []
        }
    },
    created() {
        this.getData()
    },
    methods: {
        getData() {
            // 模拟列表数据
            const mockData = []
            for (i=this.data.length; i<50; i++) {
                mockData.push({
                    id: i.toString(),
                    text: i.toString() + 'aaaa'
                })
            }
            this.data = Array.prototype.concat(this.data, mockData)
            this.totalNum = 1000
        }
    }
}
</script>

<style scoped> 
.outer-box {
    border: 1px solid #ddd;
    overflow-y: auto;
}
</style>

最内部区域

  1. 遍历数组data
  2. 设置每一项的高度为itemHeight(30px)
<template>
    <a-spin tip="loading" :spinning="loading">
        <!--最外层区域-->
        <div 
            class="outer-box" 
            @scroll="handleScroll" 
            :style="{height: height.toString + 'px'}"
           >
            <!--中间区域-->
            <div :style="{height: (itemHeight * item.totalNum).toString() + 'px'}">
                <!--最内层区域-->
                <div 
                    v-for="item in data" 
                    :key="item.id" 
                    :style="{height: itemHeight.toString() + 'px'}"
                >
                    {{ item.text }}
                </div>
            </div>
        <div>
    </a-spin>
</template>

<script>
export default {
    data() {
        return {
            height: 300,
            itemHeight: 30,
            totalNum: 0,
            data: []
        }
    },
    created() {
        this.getData()
    },
    methods: {
        getData() {
            // 模拟列表数据
            const mockData = []
            for (i=this.data.length; i<50; i++) {
                mockData.push({
                    id: i.toString(),
                    text: i.toString() + 'aaaa'
                })
            }
            this.data = Array.prototype.concat(this.data, mockData)
            this.totalNum = 1000
        }
    }
}
</script>

<style scoped> 
.outer-box {
    border: 1px solid #ddd;
    overflow-y: auto;
}
</style>

完善handleScroll

import throttle from 'lodash/throttle

export default {
    methods: {
        handleScroll:  throttle((e) => {
            if (this.data.length >= this.totalNum) return 
            let { scrollTop } = e.target
            if (scrollTop + 2 * this.height >= this.totalNum * this.itemHeight) {
                this.page += 1
                this.getData()
            }
    }, 100)
    }
}