From 2574e38d9a96c40baf18ba72570098bccdfa3ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=A5=E6=B5=A9?= <37316281+yanhao98@users.noreply.github.com> Date: Sat, 7 Dec 2024 23:11:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=97=A0=E9=99=90?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=BB=84=E4=BB=B6=E5=92=8C=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...UseIntersectionObserverInfiniteLoading.vue | 86 +++++++++++++++++++ src/pages/InfiniteLoading.page.vue | 32 +++++++ .../useIntersectionObserverList.page.vue | 82 ++++++++++++++---- typed-router.d.ts | 1 + 4 files changed, 184 insertions(+), 17 deletions(-) create mode 100644 src/components/UseIntersectionObserverInfiniteLoading.vue create mode 100644 src/pages/InfiniteLoading.page.vue diff --git a/src/components/UseIntersectionObserverInfiniteLoading.vue b/src/components/UseIntersectionObserverInfiniteLoading.vue new file mode 100644 index 0000000..f0cadf5 --- /dev/null +++ b/src/components/UseIntersectionObserverInfiniteLoading.vue @@ -0,0 +1,86 @@ + + + + + + + + 没有更多了 + + + + + 加载中... + + + + + 加载失败 + 重试 + + + + + diff --git a/src/pages/InfiniteLoading.page.vue b/src/pages/InfiniteLoading.page.vue new file mode 100644 index 0000000..01a2d27 --- /dev/null +++ b/src/pages/InfiniteLoading.page.vue @@ -0,0 +1,32 @@ + + + + + {{ item.name }} + + {{ item.email }} + {{ item.body }} + + + + + + diff --git a/src/pages/useIntersectionObserverList.page.vue b/src/pages/useIntersectionObserverList.page.vue index e41607e..f6fdb0b 100644 --- a/src/pages/useIntersectionObserverList.page.vue +++ b/src/pages/useIntersectionObserverList.page.vue @@ -2,39 +2,87 @@ import { useIntersectionObserver } from '@vueuse/core'; import { ref } from 'vue'; +function checkIsVisible(el: Element, root: Element | null = null) { + if (!el) return false; + + const elRect = el.getBoundingClientRect(); + const rootRect = root + ? root.getBoundingClientRect() + : { top: 0, left: 0, bottom: window.innerHeight, right: window.innerWidth }; + + return ( + elRect.bottom >= rootRect.top && + elRect.top <= rootRect.bottom && + elRect.right >= rootRect.left && + elRect.left <= rootRect.right + ); +} + +interface Comment { + postId: number; + id: number; + name: string; + email: string; + body: string; +} +const list = ref([]); + const loading = ref(false); -const list = ref([]); +const page = ref(0); +const pageSize = 1; + const loadMore = async () => { + if (loading.value) return; + loading.value = true; - await new Promise((resolve) => setTimeout(resolve, 1000)); - const items = Array.from({ length: 5 }, (_, i) => `Item ${list.value.length + i + 1}`); - list.value.push(...items); - loading.value = false; + try { + await new Promise((resolve) => setTimeout(resolve, 1000)); + const response = await fetch( + `https://jsonplaceholder.typicode.com/comments?_page=${++page.value}&_limit=${pageSize}`, + ); + const data = await response.json(); + list.value.push(...data); + } catch (error) { + console.error('Failed to load comments:', error); + } finally { + loading.value = false; + } + await nextTick(); + if (checkIsVisible(target.value!)) { + loadMore(); + } }; const target = ref(null); const isVisible = ref(false); -const { isActive, pause, resume, isSupported } = useIntersectionObserver([target], ([entry]) => { - isVisible.value = entry?.isIntersecting || false; -}); +const { isActive, pause, resume, isSupported } = useIntersectionObserver( + target, + ([entry]) => { + isVisible.value = entry?.isIntersecting || false; + if (entry?.isIntersecting) { + loadMore(); + } + }, + // { threshold: 0.1 }, +); - - {{ item }} + + {{ item.name }} + + {{ item.email }} + {{ item.body }} + - + - + isSupported: {{ isSupported }} Element - + {{ isVisible ? 'inside' : 'outside' }} the viewport diff --git a/typed-router.d.ts b/typed-router.d.ts index 1cd9b8e..cbe21e7 100644 --- a/typed-router.d.ts +++ b/typed-router.d.ts @@ -25,6 +25,7 @@ declare module 'vue-router/auto-routes' { 'Api': RouteRecordInfo<'Api', '/api', Record, Record>, 'DataLoadersId': RouteRecordInfo<'DataLoadersId', '/data-loaders/:id', { id: ParamValue }, { id: ParamValue }>, 'IndexPage': RouteRecordInfo<'IndexPage', '/index-page', Record, Record>, + 'InfiniteLoading': RouteRecordInfo<'InfiniteLoading', '/InfiniteLoading', Record, Record>, 'MdPage': RouteRecordInfo<'MdPage', '/md-page', Record, Record>, 'SomePage': RouteRecordInfo<'SomePage', '/some-page', Record, Record>, 'TsEnumUtil': RouteRecordInfo<'TsEnumUtil', '/ts-enum-util', Record, Record>,
{{ item.email }}
{{ item.body }}