PC端实现滚动分页懒加载

思路

  • 监听列表元素的滚动事件,滚动到底部的时候,加载下一页的数据
  • 监听数据加载,判断是否已全部加载结束

实现

监听滚动事件

  • 为列表元素 listBox 绑定 scroll 事件进行监听
<div class="listBox" @scroll="watchScroll">
  <div class="sinList" v-for="(item, index) in dataList" :key="index">
    {{ item.label }}--{{ item.value }}
  div>
  <div class="dataDesc">
    {{
      dataList.length == 0
        ? "暂无数据"
        : loading && !finished
        ? "加载中"
        : "没有更多了"
    }}
  div>
div>
  • scrollTop:listBox 滚动时,向上卷曲出去的距离
  • clientHeight:listBox 滚动时,可视的高度
  • scrollHeight:listBox 的总高度
  • 滚动的过程中,scrollTop 不断变大,直至 scrollTop + clientHeight == scrollHeight 时,表示 listBox 已经滚动到了底部
  • 滚动到底部时,如果 finished 为 false,表示数据还未加载完,此时需要调用接口,加载下一页的数据
  • 加载完数据后,scrollHeight 变大
  • 继续滚动,scrollTop 变大,直至再次 scrollTop + clientHeight == scrollHeight …
  • 为了优化体验,可以使“调用接口”提前触发,即在距离底部一定距离时,就触发接口加载。
  • 此距离取个名字为安全距离 safeHeight,即 scrollTop + clientHeight >= scrollHeight - safeHeight 时,触发接口调用

PC端实现滚动分页懒加载_第1张图片

// 监听滚动事件
watchScroll(e) {
  let scrollTop = e.target.scrollTop; // listBox 滚动条向上卷曲出去的长度,随滚动变化
  let clientHeight = e.target.clientHeight; // listBox 的视口可见高度,固定不变
  let scrollHeight = e.target.scrollHeight; // listBox 的整体高度,随数据加载变化
  let saveHeight = 30; // 安全距离,距离底部XX时,触发加载
  let tempVal = scrollTop + clientHeight + saveHeight; // 向上卷曲距离 + 视口可见高度 + 安全距离
  console.log(
    "scrollTop:" +
      scrollTop +
      ";clientHeight:" +
      clientHeight +
      ";scrollHeight:" +
      scrollHeight,
    ";tempVal:" + tempVal
  );
  // 如果不加入 saveHeight 安全距离,在 scrollTop + clientHeight == scrollHeight 时,触发加载
  // 加入安全距离,相当于在 scrollTop + clientHeight >= scrollHeight - 30 时,触发加载,比前者更早触发
  if (tempVal >= scrollHeight) {
    console.log("滚动到底了");
    if (!this.finished && !this.switch) {
      // 数据加载未结束 && 未加锁
      this.getData();
    }
    this.switch = true; // 加锁,防止重复触发
  } else {
    console.log("还没有滚动到底");
  }
},

请求接口数据

  • 当 res.data 的条数 < pageSize 的时候,说明数据已经请求完毕
  • 当 dataList 的条数 == total 时,说明数据已经请求完毕
  • 以上两种情况可以将全局变量 finished 置为 true
  • 其他情况视为加载还未结束,将 finished 置为 false,页码自加 1,改变滚动锁的状态
  • 滚动锁 switch 用来防止接口被重复请求。当滚动到达底部时,请求接口时,将 switch 置为 true,即使再次触发 tempVal >= scrollHeight 也不再执行数据请求。
  • 直到请求到数据,并且判断数据还未加载完,则将 switch 置为 false,可以进入下次滚动判断
getData() {
  let params = {
    pageSize: this.pageSize,
    pageNum: this.pageNum,
  };
  this.loading = true;
  this.mockList(params).then((res) => {
    this.loading = false;
    if (res.code == 200) {
      if (res.data && res.data.length > 0) {
        this.dataList = this.dataList.concat(res.data);
        this.total = res.totalCount;
        if (
          res.data.length < this.pageSize ||
          this.dataList.length >= this.total
        ) {
          // 返回结果条数少于请求条数,认为已结束
          // 目前数据条数等于总条数,认为已结束
          this.finished = true;
        } else {
          this.finished = false;
          this.pageNum += 1; // 页码加1
          this.switch = false; // 还可以继续加载,改变锁状态
        }
      }
    }
  });
},

图示

PC端实现滚动分页懒加载_第2张图片
PC端实现滚动分页懒加载_第3张图片

完整代码

<template>
  <div class="page">
    <div class="listBox" @scroll="watchScroll">
      <div class="sinList" v-for="(item, index) in dataList" :key="index">
        {{ item.label }}--{{ item.value }}
      div>
      <div class="dataDesc">
        {{
          dataList.length == 0
            ? "暂无数据"
            : loading && !finished
            ? "加载中"
            : "没有更多了"
        }}
      div>
    div>
  div>
template>
<script>
export default {
  data() {
    return {
      pageSize: 10,
      pageNum: 1,
      total: 0,
      loading: false,
      finished: true,
      switch: false, // 加锁,防止滚动时,判断条件重复调用
      dataList: [],
    };
  },
  mounted() {
    this.getData();
  },
  methods: {
    getData() {
      let params = {
        pageSize: this.pageSize,
        pageNum: this.pageNum,
      };
      this.loading = true;
      this.mockList(params).then((res) => {
        this.loading = false;
        if (res.code == 200) {
          if (res.data && res.data.length > 0) {
            this.dataList = this.dataList.concat(res.data);
            this.total = res.totalCount;
            if (
              res.data.length < this.pageSize ||
              this.dataList.length >= this.total
            ) {
              // 返回结果条数少于请求条数,认为已结束
              // 目前数据条数等于总条数,认为已结束
              this.finished = true;
            } else {
              this.finished = false;
              this.pageNum += 1; // 页码加1
              this.switch = false; // 还可以继续加载,改变锁状态
            }
          }
        }
      });
    },
    // 模拟请求接口
    mockList(params) {
      console.log("请求接口 params", params);
      return new Promise((resolve, reject) => {
        let data = [];
        for (let i = 0; i < 58; i++) {
          data.push({
            value: "value" + i,
            label: "label" + i,
          });
        }
        // 模拟分页
        let tempArr = data.splice(
          params.pageSize * (params.pageNum - 1),
          params.pageSize * params.pageNum
        );
        let res = {
          data: tempArr,
          totalCount: data.length,
          pageSize: params.pageSize,
          pageNum: params.pageNum,
          code: 200,
          msg: "success",
        };
        setTimeout(() => {
          resolve(res);
        }, 100);
      });
    },
    // 监听滚动事件
    watchScroll(e) {
      let scrollTop = e.target.scrollTop; // listBox 滚动条向上卷曲出去的长度,随滚动变化
      let clientHeight = e.target.clientHeight; // listBox 的视口可见高度,固定不变
      let scrollHeight = e.target.scrollHeight; // listBox 的整体高度,随数据加载变化
      let saveHeight = 30; // 安全距离,距离底部XX时,触发加载
      let tempVal = scrollTop + clientHeight + saveHeight; // 向上卷曲距离 + 视口可见高度 + 安全距离
      console.log(
        "scrollTop:" +
          scrollTop +
          ";clientHeight:" +
          clientHeight +
          ";scrollHeight:" +
          scrollHeight,
        ";tempVal:" + tempVal
      );
      // 如果不加入 saveHeight 安全距离,在 scrollTop + clientHeight == scrollHeight 时,触发加载
      // 加入安全距离,相当于在 scrollTop + clientHeight >= scrollHeight - 30 时,触发加载,比前者更早触发
      if (tempVal >= scrollHeight) {
        console.log("滚动到底了");
        if (!this.finished && !this.switch) {
          // 数据加载未结束 && 未加锁
          this.getData();
        }
        this.switch = true; // 加锁,防止重复触发
      } else {
        console.log("还没有滚动到底");
      }
    },
  },
};
script>
<style lang="scss" scoped>
.page {
  width: 100%;
  height: 100vh;
  background: #f1f1f1;
  .listBox {
    background: #fff;
    width: 600px;
    height: 300px;
    overflow: auto;
    padding: 20px;
    box-sizing: border-box;
    .sinList {
      height: 40px;
      line-height: 40px;
    }
    .dataDesc {
      color: #999;
      text-align: center;
    }
  }
}
style>

你可能感兴趣的:(思路记录,javascript,前端)