Vue踩坑之旅(四)—— 自定义指令实现滚动加载

1,525 阅读2分钟

这几天在做商城首页的商品列表,商品卡片的数量很多,如果一次性加载那么多,加载较慢,而且用户体验不好。所以使用鼠标无限滚动加载效果更好。 实现滚动加载的方式有很多,有现成的组件 InfiniteScroll,但是一些非主流浏览器无法触发,还是自己动手写一写吧。

实现滚动加载的核心:滚动条高度 + 浏览器窗口高度 >= 内容高度 - 阈值

  • document.body.scrollTop 滚动条滚动的距离 (这个有兼容性问题,兼容性写法)
    let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
  • window.innerHeight 浏览器窗口高度
  • document.body.scrollHeight 内容高度 (兼容性写法)
    let bodyHeight = document.body.scrollHeight || document.documentElement.scrollHeight;
  1. 自定义指令 v-scroll
directives: {
  /**
   * 滚动加载的自定义指令
   */
  scroll: {
    bind(el, binding, vnode) {
      window.addEventListener('scroll', vnode.context.scrollLoad)
    },
    unbind(el, binding, vnode) {
      window.removeEventListener('scroll', vnode.context.scrollLoad)
    }
  }
},
methods: {
  scrollLoad() {
    //滚动条高度(页面被卷去高度)
    let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
    //文档高度
    let bodyHeight = document.body.scrollHeight || document.documentElement.scrollHeight;
    // 滚动条高度 + 浏览器高度 >= 文档高度 - 阈值
    if (scrollTop + window.innerHeight >= bodyHeight - 300) {
      // 判断请求发送标志位,避免重复请求
      if (this.loading) return;
      // 加载的页码和每次滚动加载的数量,中data中声明
      if (this.listIndex * this.PageQty >= this.total) return;
      this.listIndex++
      // 获取商品数据的异步操作
      this.getProductList(this.listIndex)
    }
  }
}

这里需要注意:

  1. 因为发送请求和滚动事件的方法定义在了组件的 methods中,需要拿到Vue实例,但在自定义指令里,不能通过 this 拿到Vue实例,而是通过指令钩子函数的第三个参数 vnodecontext 属性拿

  2. 必须要在unbind钩子中解绑滚动加载事件,否则在其他页面也会被触发。

使用时,因为基于文档高度和滚动条高度,绑在哪里无所谓。

<template>
  <div id="index" v-scroll>
    <ul>
      <li v-for="(item, index) in productList" :key="index">
      </li>
    </ul>
  </div>
</template>