开发预览 更新于 2026-05-10

InfiniteScroll 无限滚动

滚动到底部时自动触发加载下一页,基于 IntersectionObserver。

基础用法

把列表放在组件 slot 内即可;当组件底部的 sentinel 进入视口(提前 threshold 像素)时触发 loadloading 抑制重复触发,finished 显示「没有更多了」。

背景 视口
  • 第 1 项
  • 第 2 项
  • 第 3 项
  • 第 4 项
  • 第 5 项
  • 第 6 项
  • 第 7 项
  • 第 8 项
  • 第 9 项
  • 第 10 项
  • 第 11 项
  • 第 12 项
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfInfiniteScroll } from '@chufix-design/vue';

const items = ref<number[]>(Array.from({ length: 12 }, (_, i) => i + 1));
const loading = ref(false);
const finished = ref(false);

function loadMore() {
  if (loading.value || finished.value) return;
  loading.value = true;
  setTimeout(() => {
    const start = items.value.length + 1;
    items.value.push(...Array.from({ length: 12 }, (_, i) => start + i));
    loading.value = false;
    if (items.value.length >= 60) finished.value = true;
  }, 600);
}
</script>
<template>
  <div style="height: 280px; overflow-y: auto; border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
    <CfInfiniteScroll
      :loading="loading"
      :finished="finished"
      :threshold="80"
      @load="loadMore"
    >
      <ul style="margin: 0; padding: 0; list-style: none; display: grid; gap: 4px;">
        <li
          v-for="i in items"
          :key="i"
          style="padding: 8px 12px; background: var(--bg-2); border-radius: 4px;"
        >第 {{ i }} 项</li>
      </ul>
    </CfInfiniteScroll>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfInfiniteScroll } from '@chufix-design/vue';

const items = ref<number[]>(Array.from({ length: 12 }, (_, i) => i + 1));
const loading = ref(false);
const finished = ref(false);

function loadMore() {
  if (loading.value || finished.value) return;
  loading.value = true;
  setTimeout(() => {
    const start = items.value.length + 1;
    items.value.push(...Array.from({ length: 12 }, (_, i) => start + i));
    loading.value = false;
    if (items.value.length >= 60) finished.value = true;
  }, 600);
}
</script>
<template>
  <div style="height: 280px; overflow-y: auto; border: 1px solid var(--line-1); border-radius: 8px; padding: 8px;">
    <CfInfiniteScroll
      :loading="loading"
      :finished="finished"
      :threshold="80"
      @load="loadMore"
    >
      <ul style="margin: 0; padding: 0; list-style: none; display: grid; gap: 4px;">
        <li
          v-for="i in items"
          :key="i"
          style="padding: 8px 12px; background: var(--bg-2); border-radius: 4px;"
        >第 {{ i }} 项</li>
      </ul>
    </CfInfiniteScroll>
  </div>
</template>
import { useState } from 'react';
import { CfInfiniteScroll } from '@chufix-design/react';

export default function Demo() {
  const [items, setItems] = useState<number[]>(Array.from({ length: 12 }, (_, i) => i + 1));
  const [loading, setLoading] = useState(false);
  const [finished, setFinished] = useState(false);

  function loadMore() {
    if (loading || finished) return;
    setLoading(true);
    setTimeout(() => {
      const start = items.length + 1;
      items.push(...Array.from({ length: 12 }, (_, i) => start + i));
      setLoading(false);
      if (items.length >= 60) setFinished(true);
    }, 600);
  }
  return (
    <>
      <div style={{ height: 280, overflowY: "auto", border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
          <CfInfiniteScroll loading={loading} finished={finished} threshold={80} onLoad={loadMore} >
            <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "grid", gap: 4 }}>
              <li v-for="i in items" key={i} style={{ padding: "8px 12px", background: "var(--bg-2)", borderRadius: 4 }} >第 {i} 项</li>
            </ul>
          </CfInfiniteScroll>
        </div>
    </>
  );
}
import { useState } from 'react';
import { CfInfiniteScroll } from '@chufix-design/react';

export default function Demo() {
  const [items, setItems] = useState<number[]>(Array.from({ length: 12 }, (_, i) => i + 1));
  const [loading, setLoading] = useState(false);
  const [finished, setFinished] = useState(false);

  function loadMore() {
    if (loading || finished) return;
    setLoading(true);
    setTimeout(() => {
      const start = items.length + 1;
      items.push(...Array.from({ length: 12 }, (_, i) => start + i));
      setLoading(false);
      if (items.length >= 60) setFinished(true);
    }, 600);
  }
  return (
    <>
      <div style={{ height: 280, overflowY: "auto", border: "1px solid var(--line-1)", borderRadius: 8, padding: 8 }}>
          <CfInfiniteScroll loading={loading} finished={finished} threshold={80} onLoad={loadMore} >
            <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "grid", gap: 4 }}>
              <li v-for="i in items" key={i} style={{ padding: "8px 12px", background: "var(--bg-2)", borderRadius: 4 }} >第 {i} 项</li>
            </ul>
          </CfInfiniteScroll>
        </div>
    </>
  );
}

API

属性类型默认值说明
loadingbooleanfalse加载中,期间不再触发 load
finishedbooleanfalse已无更多数据
thresholdnumber100sentinel 进入视口前 N 像素就触发(rootMargin)

Events

Vue 事件React 回调载荷类型说明
loadonLoadsentinel 进入视口时触发;应在此回调内加载下一批数据

Slots

Slot说明
default列表内容
loading自定义「加载中」提示
finished自定义「没有更多了」提示

反馈与讨论

InfiniteScroll 无限滚动 的讨论

0
0 / 600
正在加载评论...