# PressList 列表

瀑布流滚动加载,用于展示长列表,当列表即将滚动到底部时,会触发事件并加载更多列表项。

# 引入

import PressList from 'press-ui/press-list/press-list';

export default {
  components: {
    PressList,
  }
}

# 代码演示

# 基本用法

List 组件通过 loadingfinished 两个变量控制加载状态,当组件滚动到底部时,会触发 load 事件并将 loading 设置成 true。此时可以发起异步操作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。

<press-list
  v-model="loading"
  :finished="finished"
  :immediate-check="immediateCheck"
  finished-text="没有更多了"
  @load="load"
>
  <div
    v-for="item of list"
    :key="item.value"
  >
    {{ item.label }}
  </div>
</press-list>
function fetchData({ length, beforeLength }) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        data: Array.from({ length }).map((_, i) => {
          let value = i + beforeLength;
          if (value < 10) {
            value = `0${value}`;
          }
          return {
            label: value,
            value,
          };
        }),
      });
    }, 200);
  });
}


export default {
  data() {
    return {
      list: [],
      loading: false,
      finished: false,
      immediateCheck: false,
    };
  },
  methods: {
    load() {
      this.onFetchData();
    },
    onFetchData() {
      fetchData({
        length: 7,
        beforeLength: this.list.length,
      }).then(({ data }) => {
        this.list = [
          ...this.list,
          ...data,
        ];
        this.loading = false;
        this.finished = this.list.length > 50;
      })
        .catch(() => {});
    },
  },
};

# API

# Props

参数 说明 类型 默认值
v-model 是否处于加载状态,加载过程中不触发load事件 boolean false
finished 是否已加载完成,加载完成后不再触发load事件 boolean false
offset 滚动条与底部距离小于 offset 时触发load事件 number 300
loading-text 加载过程中的提示文案 string 加载中...
finished-text 加载完成后的提示文案 string -
immediate-check 是否在初始化时立即执行滚动位置检查 boolean true
vertical 是否竖向滚动 boolean true
finished-style 已完成自定义样式 string -
loading-style 加载中自定义样式 string -
loading-size 加载中图标尺寸 string 20px
auto-check-scroller 是否自动检测滚动元素 boolean false
scroll-top 设置竖向滚动条位置 number | string 0
scroll-left 设置横向滚动条位置 number | string 0

# Event

事件名 说明 回调参数
load 滚动条与底部距离小于 offset 时触发 -
scroll 页面滚动时触发 -

# Slots

名称 说明
default 列表内容
loading 自定义底部加载中提示
finished 自定义加载完成后的提示文案

# 在线调试

# 常见问题

# 1. List 的运行机制是什么?

List 会监听浏览器的滚动事件并计算列表的位置,当列表底部与可视区域的距离小于offset时,List 会触发一次 load 事件。

# 2. 为什么 List 初始化后会立即触发 load 事件?

List 初始化后会触发一次 load 事件,用于加载第一屏的数据,这个特性可以通过immediate-check属性关闭。

# 3. 非load事件触发的请求要将loading设置为true

如果想在页面一进入就加载数据,需要将loading手动设置为true,否则请求数据不满一屏的话,组件无法监听数据变化,不会自动加载下一页。

另外这种使用方式,需要将immediate-check设置为false,防止加载数据重复。

export default {
  data() {
    return {
      immediateCheck: false,
    }
  },
  mounted() {
    this.loading = true;
    this.onFetchData(true);
  },
}

加载数据的时候loading应该始终为true,因此 onFetchData 中不要加下面这行,这会导致 onFetchData 无法加载数据。

if (this.loading) return;

# 4. 为什么会连续触发 load 事件?

如果一次请求加载的数据条数较少,导致列表内容无法铺满当前屏幕,List 会继续触发 load 事件,直到内容铺满屏幕或数据全部加载完成。因此你需要调整每次获取的数据条数,理想情况下每次请求获取的数据条数应能够填满一屏高度。

# 5. loadingfinished 分别是什么含义?

List有以下三种状态,理解这些状态有助于你正确地使用List组件:

  • 非加载中,loadingfalse,此时会根据列表滚动位置判断是否触发load事件(列表内容不足一屏幕时,会直接触发)
  • 加载中,loadingtrue,表示正在发送异步请求,此时不会触发load事件
  • 加载完成,finishedtrue,此时不会触发load事件

在每次请求完毕后,需要手动将loading设置为false,表示加载结束。

  • 加载中,loading的值传递方向,组件内 => 组件外,变为true
  • 加载结束,loading的值传递方向,组件外 => 组件内,变为false

# 6. 虚拟列表

由于列表数据在父组件控制,press-list内部只有slot占位,并且不同列表的结构和样式千差万别,所以虚拟列表只能在父组件实现。

press-list提供了虚拟列表的简单示例 (opens new window),可以参考使用。

虚拟列表的实现原理如下:

  • 数据一定是切割过的,如果是全量数据,也就是v-for循环的是data的话,渲染的是全量Dom,虚拟列表也就无从谈起。
  • 既然数据切割过了,那么高度一定会塌陷,解决办法有下面几种:
    • 绝对定位
    • transform
    • 顶部插入占位Dom

press-pickerpress-list的示例都是用的第三种方法。

# 7. loading 传递

如果press-list在子组件,loading的值由父组件控制,可以这样传递:

<!-- 父组件 -->
<SomeComponent
  :joined-loading="joinedLoading"
  @update:joinedLoading="val => joinedLoading = val"
/>
<!-- 子组件 -->
<PressList
  v-model="innerJoinedLoading"
  @load="onLoadMore"
>
</PressList>
export default {
  props: {
    joinedLoading: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    innerJoinedLoading: {
      get() {
        return this.joinedLoading;
      },
      set(value) {
        this.$emit('update:joinedLoading', value);
      },
    },
  }
}

# 8. 滑动到底部,加载更多不触发

检查内部元素是否够长,或设置了 overflowhidden/scroll

横屏