feat: 添加深冻结库,重构无限加载页面,优化缓存逻辑并新增详情页
All checks were successful
/ depcheck (push) Successful in 1m16s
/ build-and-deploy-to-vercel (push) Successful in 1m47s
/ playwright (push) Successful in 1m55s

This commit is contained in:
严浩
2025-01-02 16:18:37 +08:00
parent 49da2ee1d8
commit d30163351a
7 changed files with 186 additions and 125 deletions

View File

@ -75,6 +75,7 @@ const load = async (why?: string) => {
pause();
}
} catch (error) {
console.error(`error :>> `, error);
currentPage--;
state.value = 'error';
}

View File

@ -1,39 +0,0 @@
<script setup lang="ts">
let showDebugError = true;
const list = ref<Record<string, never>[]>([]);
const loadData = async (page: number) => {
await new Promise((resolve) => setTimeout(resolve, 250 /* + 1000 */));
if (page === 2 && showDebugError) {
showDebugError = false;
throw new Error('Failed to load comments');
}
const response = await fetch(
`https://jsonplaceholder.typicode.com/comments?_page=${page}&_limit=1&timestamp=${Date.now()}`,
);
const data = await response.json();
list.value.push(...data);
return { hasMore: list.value.length < 5 };
};
</script>
<template>
<Button label="刷新" @click="list.splice(0, list.length) && $refs.infiniteLoading?.refresh()" mb-4 />
<Card v-for="item in list" :key="item.id" class="mb-[16px]">
<template #title>{{ item.name }}</template>
<template #content>
<p class="mt-[8px]">{{ item.body }}</p>
</template>
<template #subtitle>id:{{ item.id }} </template>
<template #footer>{{ item.email }}</template>
</Card>
<div border="1 px solid red">
{{ { 'list.length': list.length } }}
</div>
<UseIntersectionObserverInfiniteLoading :async-load="loadData" ref="infiniteLoading">
<!-- <template #error="{ retry }">
<Button fluid @click="retry">Retry</Button>
</template> -->
</UseIntersectionObserverInfiniteLoading>
<ScrollTop />
</template>

View File

@ -0,0 +1,8 @@
<script setup lang="ts">
const route = useRoute();
</script>
<template>
<h1>Detail</h1>
<p>id: {{ route.query.id }}</p>
</template>

View File

@ -0,0 +1,73 @@
<script lang="ts">
const structuredClone = window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj)));
const K_INITIAL_CACHE = deepFreeze({
page: 0,
list: [] as Record<string, never>[],
hasMore: true,
});
const cache = structuredClone(K_INITIAL_CACHE);
</script>
<script setup lang="ts">
defineOptions({
beforeRouteEnter: (to, from) => {
if (from.name !== 'InfiniteLoadingDetail') Object.assign(cache, structuredClone(K_INITIAL_CACHE)); // 如果来的页面不是详情页,清空缓存。
},
beforeRouteLeave: (to, from) => {
if (to.name !== 'InfiniteLoadingDetail') Object.assign(cache, structuredClone(K_INITIAL_CACHE)); // 如果去的页面不是详情页,清空缓存。
},
});
let isPage2ErrorVisible = true;
const lastCache = structuredClone(cache); // 要在 `loadData` 之前获取,因为 `loadData` 会修改 `cache.page`。
const list = shallowRef<Record<string, never>[]>(lastCache.list);
const loadData = async (page: number) => {
if (!lastCache.hasMore) return { hasMore: false };
await new Promise((resolve) => setTimeout(resolve, 250 + 1000));
if (page === 2 && isPage2ErrorVisible) {
isPage2ErrorVisible = false;
throw new Error('Failed to load comments. Because page === 2');
}
const response = await fetch(
`https://jsonplaceholder.typicode.com/comments?_page=${page + lastCache.page}&_limit=1&timestamp=${Date.now()}`,
);
const data = await response.json();
list.value = list.value.concat(data);
const hasMore = list.value.length < 5;
{
cache.page = page;
cache.list = toRaw(list.value);
cache.hasMore = hasMore;
}
return { hasMore };
};
</script>
<template>
<Button label="刷新" @click="list.splice(0, list.length) && $refs.infiniteLoading?.refresh()" mb-4 />
<Card
v-for="item in list"
:key="item.id"
class="mb-[16px]"
@click="$router.push({ name: 'InfiniteLoadingDetail', query: { id: item.id } })"
>
<template #title>{{ item.name }}</template>
<template #content>
<p class="mt-[8px]">{{ item.body }}</p>
</template>
<template #subtitle>id:{{ item.id }} </template>
<template #footer>{{ item.email }}</template>
</Card>
<div border="1 px solid red">
{{ { 'list.length': list.length } }}
</div>
<UseIntersectionObserverInfiniteLoading :async-load="loadData" ref="infiniteLoading">
<!-- <template #error="{ retry }">
<Button fluid @click="retry">Retry</Button>
</template> -->
</UseIntersectionObserverInfiniteLoading>
<ScrollTop />
</template>