上拉无限加载方案

上拉加载可行性实现方案及性能比对

关于上拉加载方案的确定,有多种实现方式,最初考虑监听页面的滚动事件,对消息列表最后一条消息位置进行计算,判断当其出现在视口中时,请求接口加载新的消息数据,视图持续更新,每次需要选中消息列表的最后一条消息进行计算,处理有些复杂。综合考虑各种方案及实现简便性,下面两种方案可以进行讨论

方案1

监听页面滚动事件,获取页面根元素到视口顶部的距离x,获取元素在视口中的高度y,获取元素的实际高度z:

  • 消息列表在滚动过程中未加载到底部时,始终有x+y<z
  • 当消息列表加载到底部时,有x+y===z

为了防止频繁触发滚动事件的监听事件,对滚动事件进行防抖处理,监听事件频繁触发时,每隔着200ms再执行一次任务
[这里有一张图片]

实现的代码也很简洁

  window.addEventListener('scroll', throttle(scrollEventHandler, 100))
  function scrollEventHandler () {
    let scrollTop = document.documentElement.scrollTop //元素顶部到视口顶部的距离
    let clientHeight = document.documentElement.clientHeight //获取元素在视口中的高度,包括内边距,不过包括水平滚动条/边框/外边距
    let scrollHeight = document.documentElement.scrollHeight //获取元素实际的高度,包括内边距,不过包括水平滚动条/边框/外边距
    if (scrollHeight === scrollTop + clientHeight) {
      //调用请求数据接口
    }
  }
  function debounce(fn, delay) {
    let timer = delay
    return function () {
      let context = this
      let args = arguments
      clearTimeout(timer)
      timer = setTimeout(function() {
        fn.apply(context, args)
      }, delay)
    }
  }

方案2

  • 考虑到监听消息列表的最后一条消息并计算其是否出现在视口,需要在每次dom更新完毕后重新选择最后一条消息进行计算其是否出现在视口中,因此想到一种比较便捷的实现方法。
  • 封装一个公用的底部bar组件,始终置于消息列表尾部,根据 Intersection Observer 方法判断其是否出现在视口中,当其出现在视口中,表示当前消息页面已经加载到页面底部。

    [这里也有一张图片]
  • 这个种实现方案不需要监听页面的滚动事件,在实现上也比较方便。考虑到api的兼容性,以及锁屏业务的实际场景,不需要再引入额外的polyfill文件

封装的公用底bar组件,可以传入当底bar出现在视口中后相应的加载事件,在消息加载完毕后停止对底bar元素的观察

<template>
  <div v-if="isShow" class="bottom-bar">
    <div v-if="status==`loading`" class="bottom-bar-loading">
      <i class="bottom-bar-loading-icon"/>
      <span class="bottom-bar-txt loading-txt">正在加载...</span>
    </div>
    <span v-if="status==`completed`" class="bottom-bar-txt">已显示全部消息</span>
    <span v-if="status==`poor-network`" class="bottom-bar-txt">网络信号差,请重试</span>
    <span v-if="status==`no-connection`" class="bottom-bar-txt">无网络连接,请设置网络</span>
    <span v-if="status==`no-service`" class="bottom-bar-txt">服务器异常,请上划重试</span>
  </div>
</template>
<script>
  export default {
    name: 'BottomBar',
    props: {
      isShow: {
        type: Boolean,
        default: true
      },
      status: {
        type: String,
        default: 'loading'
      },
      loadMethod: {
        type: Function,
        required: true
      },
      observerConfig: {
        type: Object,
        required: true,
        default: {
          threshold: 0.5
        }
      }
    },
    computed: {
      observer() {
        return new IntersectionObserver(([entry]) => {
          if(entry && this.isShow && entry.isIntersecting) {
            this.loadMethod()
          }
        }, this.observerConfig)
      }
    },
    mounted() {
      this.observer.observe(this.$el) 
    },
    methods: {
      unobserver: function() {
        this.observer.unobserve(this.$el)
      }
    }
  }
</script>

父组件:

<template>
    <BottomBar :status="status" :load-method="getLikes" ref="bar"/>
</template>
<script>
  import BottomBar from './BottomBar'
  export default {
    components: {
      BottomBar
    },
    data() {
      return {
        status: 'loading'
      }
    },
    created() {
      //模板渲染成html前调用,初始化某些属性值,渲染成视图
    },
    computed(){
    },
    mounted() {  
      //模板渲染成html后调用,初始化页面完成后,对htmldom节点进行一些操作
    },
    methods: {
    }
  }
</script>

无限滚动时,最好在页面底部有一个页尾栏(又称sentinels)。一旦页尾栏可见,就表示用户到达了页面底部,从而加载新的条目放在页尾栏前面。这样做的好处是,不需要再一次调用observe()方法,现有的IntersectionObserver可以保持使用。

IntersectionObserver API 使用教程

判断元素是否在视窗之内

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 言 午 小水滴吃飽後,她睡在搖藍裏,睜著兩只大眼望著我。她的眼睛像一彎靜靜的湖水,...
    言午57阅读 2,532评论 0 3
  • 我曾想七夕和你去做蛋糕 我曾想十一月拉你出去旅游 我曾想明年陪你去学习厨艺,后年……大后年……总在一起努力奋斗。 ...
    蘑菇团团阅读 1,761评论 4 3
  • 构思这篇文章已经有些日子了,今日朋友请吃饭看电影来晚了一会儿,市中心恰恰碰到一对姐妹花在卖唱,旁边一块牌子大概写着...
    风流人物阅读 4,062评论 0 1
  • 稍了解公司法的伙伴都知道,有限责任公司与股份有限公司是公司的两个基本形态。那么,今天要探讨的股份合作公司是哪来的?...
    lawyer无畏阅读 2,540评论 0 1
  • 一个萝卜一个坑是最基本的标配,当少了一个萝卜,多了一个坑就悲剧了,现在的我们就是这个样子,工作量没走减少反而多了,...
    李苏珊阅读 1,027评论 0 0