Skip to content

在使用vant组件库写移动端网页时,vant组件库并没有提供相关上拉加载的组件,因此只能自己手写一个,在了解了两种方案 (1)计算滚动条滚动的距离 (2)使用IntersetionObserver API 的简易程度后,决定使用IntersetionObserver API来实现上拉加载,这个方案比第一种简单太多了。 要完成上拉加载,我们首先需要对页面的布局做相应的处理,具体布局如下:

vue
<div class="team-container">
    <van-search
      v-model="searchValue.searchText"
      show-action
      placeholder="搜索队伍"
      shape="round"
      @search="handleSearchTeam"
    >
      <template #action>
        <div @click="addTeam">新增</div>
      </template>
    </van-search>
    <div class="team-list">
      <team-card-list
        ref="cardListRef"
        :team-list="teamList"
        :fetch-data="fetchData"
        @join-team="doJoinTeam"
        @update-team="doUpdateTeam"
      />
    </div>
  </div>

页面tabbar部分为固定定位,脱离了文档流,搜索框为粘性定位,不会因为页面的滚动而滚动,因此只需要计算出剩下内容的高度,并设置overflow:scroll,并将父容器设置为overflow:hidden,这样就初步具备了上拉加载的可能。

less
.team-container {
 overflow: hidden;
 height: 100%;

 .team-list {
  overflow-y: scroll;
  height: 100%;
 }
}

做好上面的步骤以后,我们还需要一个媒介,当这个媒介显示时,我们才开始触发上拉加载功能,这样就用到了IntersetionObserver API,当这个媒介在浏览器中显示时,这个API会监听到他的出现。这里,我选择了vant组件库提供的loading组件,需要注意的是,这个loading组件我们需要将它放在我们展示的所有内容的最后,不然IntersetionObserver API可能监听不到它的出现,类似下面:

vue
<template v-for="user in userList" :key="user.id">
    <van-card
          :desc="user.profile"
          :title="`${user.username} (${user.planetCode})`"
        >
          <template #thumb>
            <van-image :src="user.avatarUrl" round width="80" height="80" />
          </template>

          <template #tags>
            <template v-for="(tag, index) in user.tags" :key="index">
              <van-tag type="primary">{{ tag }}</van-tag>
            </template>
          </template>
        </van-card>
      </template>
      <div class="loading">
      <van-loading
          type="spinner"
          size="24px"
          v-loading="fetchData"
          v-if="isShow"
      />
   </div>

做好以上布局之后,开始使用自定义指定来监听loading组件的出现,之所以使用自定义指令,是因为编写自定义指令在全局注册之后无论哪个页面都能使用。 使用mounted这个钩子函数使得使用自定义指令的元素在挂载完成后调用,我们在使用自定义指令时可以传递一个可执行函数,我们在mounted这个钩子函数中开启监听loading组件的显示与隐藏,当显示时执行我们传递的可执行函数来加载新的数据,这样子就完成上拉加载了。

typescript
import type { Directive } from "vue";
import { useSettingStore } from "@/store/modules/setting";
type IPageInfo = {
  pageNumber: number;
  pageSize: number;
};
const settingStore = useSettingStore();
const vUpLoading: Directive<HTMLElement, (pageInfo?: IPageInfo) => void> = {
  mounted(el, binding) {
    let pageNumber = 1;
    const pageSize = 10;
    const observer = new IntersectionObserver((entries) => {
      const entry = entries[0];
      if (entry.intersectionRatio > 0) {
        settingStore.setLoading(true);
        binding.value({
          pageNumber: ++pageNumber,
          pageSize,
        });
      }
    });
    observer.observe(el);
  },
};

export default vUpLoading;

编写好这个指令之后,在主运行文件注册一下

typescript
import vUpLoading from "./directives/up-loading";
const app = createApp(App);
app.directive("loading", vUpLoading);

注册完成之后,直接在页面元素使用就行了

vue
<van-loading
  type="spinner"
  size="24px"
  v-loading="fetchData"
  v-if="isShow"
/>