# 【优化】 -封装一个自动加载更多的组件

# 场景

不管是小程序还是h5项目都离不开一个场景就是纯列表页面,没当我们遇到这种需求的时候都需要写一些重复的逻辑,例如loadmore上拉加载下一页数据,那我们可以将这部分逻辑抽离成一个组件,方便后序的开发。

项目中使用nextId来判断是否有下一页的数据,如果后端不返回nextId则表示没有下一页的数据

# 组件的封装

首先我们项目使用的是vue-infinite-scroll这个比较轻量的滑动库来实现加载效果




 










































































































<template>
  <div
    class="list"
    :class="[isEnd && result.length === 0 ? 'flex-row col-center row-center' : '']"
    v-infinite-scroll="loadmore"
    infinite-scroll-disabled="loading"
    infinite-scroll-distance="50"
    infinite-scroll-immediate-check="false">
    <div v-for="(item, index) in result" :key="index">
      <Card1
        v-if="cardType === 'card1'"
        :index="index + 1"
        :data="item" />
      <Card2
        v-if="cardType === 'card2'"
        :index="index + 1"
        :data="item" />
    </div>

    <div v-if="isEnd && result.length === 0">暂无数据</div>
  </div>
</template>

<script>
import Card1 from '~/components/Card1'
import Card2 from '~/components/Card2'

export default {
  props: {
    cardType: {
      type: String,
      required: true,
    },
    handler: {
      type: Function,
      required: true,
    },
  },
  components: {
    Card1,
    Card2,
  },
  data() {
    return {
      result: [],
      loading: false,
      isEnd: false,
      nextId: '',
      extra: {},
    }
  },
  methods: {
    /**
     * 刷新数据
     */
    refresh() {
      this.result = []
      this.nextId = ''
      this.isEnd = false
      this.fetch()
    },
    /**
     * 加载更多
     */
    loadmore() {
      this.fetch()
    },
    /**
     * 获取数据
     */
    fetch() {
      if (this.isEnd || this.loading) {
        return
      }
      this.setLoading(true)

      Promise.resolve()
        .then(() => this.handler({ nextId: this.nextId }))
        .then((res) => {
          this.setLoading(false)
          this.nextId = res.nextId || ''
          this.isEnd = !res.nextId
          this.extra = res.extra || {}
          this.result = this.result.concat(res.result)
        })
        .catch((error) => {
          console.log('List Error', error)
          this.setLoading(false)
        })
    },
    /**
     * 显示隐藏loading
     * @param {Boolean} loading 加载状态
     */
    setLoading(loading) {
      this.loading = loading

      if (loading) {
        this.$toast.loading({
          mask: true,
          duration: 0,
        })
      } else {
        this.$toast.clear()
      }
    },
  },
}
</script>

我们看下代码,在使用list组件的时候需要传入一个cardType表示当前展示的卡片类型,而handler使我们请求数据的方法,一般用的是vuex中的action

然后对一些请求逻辑进行封装和计算,例如判断是否没有更多新数据isEnd, 请求的时候展示loading效果,上拉加载更多数据等逻辑

我们接下来看下封装这个组件之后开发效率提高了多少




 
























<template>
  <list ref="list" cardType="card1" :handler="handler" />
</template>

<script>
import { mapActions } from 'vuex'
import List from '~/components/list'

export default {
  components: {
    List,
  },
  methods: {
    ...mapActions({
      getData: 'test/getData',
    }),
    handler(opt) {
      return this.getData(Object.assign({}, opt, {
        id: this.$route.params.id, // 这里可以放一些请求需要的参数,如id
      })).then((res) => res)
    },
  },
  mounted() {
    this.$refs.list.refresh() // 获取组件的时候,调用组件的refresh方法开始请求数据
  }
}
</script>

我们可以发现封装了加载更多的逻辑之后,我们只需要传入获取数据的方法和当前页面展示卡片的类型就可以实现一个列表页面了,而且代码量很少,so easy too happy