vue loadmore组件上拉加载更多功能示例代码

最近在做移动端h5页面,所以分页什么的就不能按照传统pc端的分页器的思维去做了,这么小的屏幕去点击也不太方便一般来讲移动端都是上拉加载更多,符合正常使用习惯。

首先简单写一下模板部分的html代码,,很简单清晰的逻辑:

<template>
 <div class="loadmore">
  <div class="loadmore__body">
   <slot></slot>
  </div>
  <div class="loadmore__footer">
   <span v-if="loading">
    <i class="tc-loading"></i>
    <span>正在加载</span>
   </span>
   <span v-else-if="loadable">上拉加载更多</span>
   <span v-else>没有更多了</span>
  </div>
 </div>
</template>

然后就是业务部分了

在动手写组件之前,先理清需求:

加载页面 -> 滑到底部 -> 上拉一定距离 -> 加载第二页 -> 继续前面步骤 -> 没有更多

这是一个用户交互逻辑,而我们需要将其映射为代码逻辑:

首屏自动加载第一页 -> 滑动到底部&&按下时候滑动距离Y轴有一定偏移量 -> 请求后端加载第二页 -> 根据返回字段判断是否还有下一页

有了代码逻辑,主干就出来了,加载和判断由事件来控制,而又作为一个vue组件,我们需要配合vue生命周期来挂载事件和销毁事件

export default {
  mounted() {
    // 确定容器
    // 容器绑定事件
  },
  beforeDestory() {
    // 解绑事件
  },
}

如果没有解绑的话,每次你加载组件,就会绑定一次事件…

然后我们需要一些核心事件回调方法来在合适的时间加载数据渲染页面, 回想一下,第一我们需要http获取数据的load函数,然后我们需要三个绑定事件的回调函数pointDown(), pointMove(), pointUp(),分别对应用户按下、移动、弹起手指操作:

export default {
  ···
  methods:{
   /**
    * 加载一组数据的方法
    */
   load() {
     // 设置options
    this.$axios.request(options).then((res) => {
      // 获取数据后的处理
    }).catch((e) => {
     // 异常处理
    })
   },
    /**
    * 鼠标按下事件处理函数
    * @param {Object} e - 事件对象
    */
   pointerdown(e) {
    // 获取按下的位置
    this.pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
   },
    /**
    * 鼠标移动事件处理函数
    * @param {Object} e - 事件对象
    */
   pointermove(e) {
    const container = this.$container
    const pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
    const moveY = pageY - this.pageY
    // 如果已经向下滚动到页面最底部
    if (moveY < 0 && (container.scrollTop + Math.min(
     global.innerHeight,
     container.clientHeight,
    )) >= container.scrollHeight) {
     // 阻止原生的上拉拖动会露出页面底部空白区域的行为(主要针对iOS版微信)
     e.preventDefault()
     // 如果上拉距离超过50像素,则加载下一页
     if (moveY < -50) {
      this.pageY = pageY
      this.load()
     }
    }
   },
    /**
    * 鼠标松开事件处理函数
    */
   pointerup() {
    // 这边就是取消拖动状态,需要注意在拖动过程中不要再次触发一些事件回调,否侧乱套
    this.dragging = false
   },
  },
  ···
}

基本上主干已经算完工了,一些props传入或者一些逻辑控制细节需要再额外添加,贴出整个组件的源码:

<template>
 <div class="loadmore">
  <!-- <div class="loadmore__header"></div> -->
  <div class="loadmore__body">
   <slot></slot>
  </div>
  <div class="loadmore__footer">
   <span v-if="loading">
    <i class="tc-loading"></i>
    <span>正在加载</span>
   </span>
   <span v-else-if="loadable">上拉加载更多</span>
   <span v-else>没有更多了</span>
  </div>
 </div>
</template>
<script type="text/babel">
 import axios from 'axios'
 const CancelToken = axios.CancelToken
 export default {
  data() {
   return {
    /**
     * 总页数(由服务端返回)
     * @type {number}
     */
    count: 0,
    /**
     * 是否正在拖拽中
     * @type {boolean}
     */
    dragging: false,
    /**
     * 已加载次数
     * @type {number}
     */
    times: 0,
    /**
     * 已开始记载
     * @type {boolean}
     */
    started: false,
    /**
     * 正在加载中
     * @type {boolean}
     */
    loading: false,
   }
  },
  props: {
   /**
    * 初始化后自动开始加载数据
    */
   autoload: {
    type: Boolean,
    default: true,
   },
   /**
    * 离组件最近的可滚动父级元素(用于监听事件及获取滚动条位置)
    */
   container: {
    // Selector or Element
    default: 'body',
   },
   /**
    * 禁用组件
    */
   disabled: {
    type: Boolean,
    default: false,
   },
   /**
    * Axios请求参数配置对象
    * {@link https://github.com/mzabriskie/axios#request-config}
    */
   options: {
    type: Object,
    default: null,
   },
   /**
    * 起始页码
    */
   page: {
    type: Number,
    default: 1,
   },
   /**
    * 每页加载数据条数
    */
   rows: {
    type: Number,
    default: 10,
   },
   /**
    * 数据加载请求地址
    */
   url: {
    type: String,
    default: '',
   },
  },
  computed: {
   /**
    * 是否可以加载
    * @returns {boolean} 是与否
    */
   loadable() {
    return !this.disabled && (!this.started || (this.page + this.times) <= this.count)
   },
  },
  mounted() {
   let container = this.container
   if (container) {
    if (typeof container === 'string') {
     container = document.querySelector(container)
    } else if (!container.querySelector) {
     container = document.body
    }
   }
   if (!container) {
    container = document.body
   }
   this.$container = container
   this.onPointerDown = this.pointerdown.bind(this)
   this.onPointerMove = this.pointermove.bind(this)
   this.onPointerUp = this.pointerup.bind(this)
   if (global.PointerEvent) {
    container.addEventListener('pointerdown', this.onPointerDown, false)
    container.addEventListener('pointermove', this.onPointerMove, false)
    container.addEventListener('pointerup', this.onPointerUp, false)
    container.addEventListener('pointercancel', this.onPointerUp, false)
   } else {
    container.addEventListener('touchstart', this.onPointerDown, false)
    container.addEventListener('touchmove', this.onPointerMove, false)
    container.addEventListener('touchend', this.onPointerUp, false)
    container.addEventListener('touchcancel', this.onPointerUp, false)
    container.addEventListener('mousedown', this.onPointerDown, false)
    container.addEventListener('mousemove', this.onPointerMove, false)
    container.addEventListener('mouseup', this.onPointerUp, false)
   }
   if (this.autoload) {
    this.load()
   }
  },
  // eslint-disable-next-line
  beforeDestroy() {
   const container = this.$container
   if (global.PointerEvent) {
    container.removeEventListener('pointerdown', this.onPointerDown, false)
    container.removeEventListener('pointermove', this.onPointerMove, false)
    container.removeEventListener('pointerup', this.onPointerUp, false)
    container.removeEventListener('pointercancel', this.onPointerUp, false)
   } else {
    container.removeEventListener('touchstart', this.onPointerDown, false)
    container.removeEventListener('touchmove', this.onPointerMove, false)
    container.removeEventListener('touchend', this.onPointerUp, false)
    container.removeEventListener('touchcancel', this.onPointerUp, false)
    container.removeEventListener('mousedown', this.onPointerDown, false)
    container.removeEventListener('mousemove', this.onPointerMove, false)
    container.removeEventListener('mouseup', this.onPointerUp, false)
   }
   if (this.loading && this.cancel) {
    this.cancel()
   }
  },
  methods: {
   /**
    * 加载一组数据的方法
    */
   load() {
    if (this.disabled || this.loading) {
     return
    }
    this.started = true
    this.loading = true
    const params = {
     currentPage: this.page + this.times,
     pageSize: this.rows,
    }
    const options = Object.assign({}, this.options, {
     url: this.url,
     cancelToken: new CancelToken((cancel) => {
      this.cancel = cancel
     }),
    })
    if (String(options.method).toUpperCase() === 'POST') {
     options.data = Object.assign({}, options.data, params)
    } else {
     options.params = Object.assign({}, options.params, params)
    }
    this.$axios.request(options).then((res) => {
     const data = res.result
     this.times += 1
     this.loading = false
     this.count = data.pageCount
     this.$emit('success', data.list)
     this.$emit('complete')
    }).catch((e) => {
     this.loading = false
     this.$emit('error', e)
     this.$emit('complete')
    })
   },
   /**
    * 重置加载相关变量
    */
   reset() {
    this.count = 0
    this.times = 0
    this.started = false
    this.loading = false
   },
   /**
    *重新开始加载
    */
   restart() {
    this.reset()
    this.load()
   },
   /**
    * 鼠标按下事件处理函数
    * @param {Object} e - 事件对象
    */
   pointerdown(e) {
    if (this.disabled || !this.loadable || this.loading) {
     return
    }
    this.dragging = true
    this.pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
   },
   /**
    * 鼠标移动事件处理函数
    * @param {Object} e - 事件对象
    */
   pointermove(e) {
    if (!this.dragging) {
     return
    }
    const container = this.$container
    const pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
    const moveY = pageY - this.pageY
    // 如果已经向下滚动到页面最底部
    if (moveY < 0 && (container.scrollTop + Math.min(
     global.innerHeight,
     container.clientHeight,
    )) >= container.scrollHeight) {
     // 阻止原生的上拉拖动会露出页面底部空白区域的行为(主要针对iOS版微信)
     e.preventDefault()
     // 如果上拉距离超过50像素,则加载下一页
     if (moveY < -50) {
      this.pageY = pageY
      this.load()
     }
    }
   },
   /**
    * 鼠标松开事件处理函数
    */
   pointerup() {
    this.dragging = false
   },
  },
 }
</script>

以上所述是小编给大家介绍的vue loadmore组件上拉加载更多功能示例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 基于vue的下拉刷新指令和滚动刷新指令

    小编最近在实现移动端列表页面显示的时候一直在思考如何实现列表的自动更新数据,对于大多数Native App或者Web App,在列表的底部增加"加载更多"的按钮也是解决这样的问题一种交互的方式,当然,这样的交互其实还好,不过根据用户的操作习惯来看,似乎滚动刷新更多数据和下拉刷新当前数据的操作方式,更符合用户对列表分页数据的读取习惯,因此,在这里小编想简单的说说,这次在小编系统中所使用的下拉刷新和滚动刷新的实现方式! 其实,这种实现数据加载的原理很简单 在滚动刷新的时候,需要在列表滚动到

  • vue.js移动端app之上拉加载以及下拉刷新实战

    上拉加载以及下拉刷新都是移动端很常见的功能,在搜索或者一些分类列表页面常常会用到. 跟横向滚动一样,我们还是采用better-scroll这个库来实现.由于better已经更新了新的版本,之前是0.几的版本,更新了一下发现,现在已经是1.2.6这个版本了,新版本多了些 比较好用的api,所以我也重写了之前的代码,用新的api来实现上拉加载以及下拉刷新. 首先把基本的样式写好,这里就略过了,然后引入better-scroll库 import BScroll from 'better-scroll'

  • vue loadmore组件上拉加载更多功能示例代码

    最近在做移动端h5页面,所以分页什么的就不能按照传统pc端的分页器的思维去做了,这么小的屏幕去点击也不太方便一般来讲移动端都是上拉加载更多,符合正常使用习惯. 首先简单写一下模板部分的html代码,,很简单清晰的逻辑: <template> <div class="loadmore"> <div class="loadmore__body"> <slot></slot> </div> <d

  • vue实现的上拉加载更多数据/分页功能示例

    本文实例讲述了vue实现的上拉加载更多数据/分页功能.分享给大家供大家参考,具体如下: 加载状态 <div v-if='has_log == 0'> <load-more tip="上拉加载" :show-loading="false" background-color="#fbf9fe"></load-more> </div> <div v-if='has_log == 1'> <

  • uniapp实现上拉加载更多功能的全过程

    目录 一.添加全部 1.在主页面中添加一列 2.改云函数 3.插件市场导入 加载中组件 二.实现上拉加载 1.云函数中可以接收参数 2.获取下拉事件 3.写触发这个下拉干嘛 总结 一.添加全部 1.在主页面中添加一列 data.unshift({ name:'全部' }) //添加一列 '全部' 2.改云函数 (累了 直接上代码)这里match匹配空对象相当于全部哈 'use strict'; const db=uniCloud.database()//1.创建引用 exports.main =

  • iOS实现无感知上拉加载更多功能的思路与方法

    目录 什么是无感知上拉加载更多 如何实现无感知上拉加载更多 网上的思路(一) 网上的思路(二) MJRefresh代码的追根朔源 总结 什么是无感知上拉加载更多 什么是无感知,这个这样理解:在网络情况正常的情况下,用户对列表进行连续的上拉时,该列表可以无卡顿不停再见新的数据. 如果要体验话,Web端很多已经做到了,比如掘金的首页,还有比如i掘金iOS的App,列表都是无感知的. 说来惭愧,写了这久的代码,还真的没有认真思考这个功能怎么实现. 如何实现无感知上拉加载更多 我在看见这位网友留言的时候

  • Flutter listview如何实现下拉刷新上拉加载更多功能

    目录 下拉刷新 RefreshIndicator 上拉加载更多 总结: 下拉刷新 在Flutter中系统已经为我们提供了google material design的刷新功能 , 样式与原生Android一样. 我们可以使用RefreshIndicator组件来实现Flutter中的下拉刷新,下面们还是先来看下如何使用吧 RefreshIndicator 构造方法: const RefreshIndicator({ Key key, @required this.child, this.disp

  • Android RecyclerView上拉加载更多功能回弹实现代码

    实现原理是使用RecyclerView的OnTouchListener方法监听滑动 在adapter里面增加两项footview 其中date.size为显示的加载条,可以自定义,date.size+1为空白的View,我们设置其高度为0 我们通过LinearLayoutManager的 findLastVisibleItemPosition判断显示的最后一条数据,如果是空白view,表示加载条已经完全展示,松开即可刷新. 回弹效果是通过在滑动时动态改变空白view的高度,达到阻尼效果 ,回弹时

  • Android Recyclerview实现上拉加载更多功能

    在项目中使用列表的下拉刷新和上拉加载更多是很常见的功能,下拉刷新我们可以用Android自带的SwipeRefreshLayout这个很好解决.但是上拉加载更多就要去找一些框架了,刚开始的时候我找到一个Mugen的github开源框架,但是有个问题,当页面能够一次加载全部item的时候,上拉加载的功能就失效了. 这是因为当界面一次能够加载完全部item的时候,继续往上拉,Recyclerview的滑动监听,中的onScrolled方法只会在页面加载的时候调用一次,只后就不会被调用了,并且dy=0

  • uni-app实现数据上拉加载更多功能实例

    目录 实现上拉加载更多 优化: 通过节流阀防止发起额外的请求 判断数据是否加载完毕 总结 实现上拉加载更多 打开项目根目录中的 pages.json 配置文件,为 subPackages 分包中的商品 goods_list 页面配置上拉触底的距离: "subPackages": [ { "root": "subpkg", "pages": [ { "path": "goods_detail/goo

  • GridView基于pulltorefresh实现下拉刷新 上拉加载更多功能(推荐)

    原理和listview一样 ,都是重写Android原生控件 Activity package com.example.refreshgridview; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.widget.GridView; import android.widget.Toast; import

  • Android RecyclerView添加上拉加载更多功能

    上一篇文章已经介绍了如何为RecyclerView添加FootView,在此基础上,要添加分页加载的功能其实已经很简单了. 上一篇文章地址:为RecyclerView添加FootView和HeadView 效果:(源码在文章结尾) 实现关键 在上一篇代码的基础上,只需要在onBindViewHolder(ViewHolder holder, int position)函数中添加一定修改就可以了,如下: @Override public void onBindViewHolder(ViewHold

随机推荐