Astro SSR模式+Directus分页功能的实现
最近用Astro开发一个项目,高强度体验下来收获颇多,先说说SSR模式和SSG在模式分页下有何区别。
首先SSG模式,需要构造所有的静态路由,需要列遍数据,如果使用api做数据查询需要select出全部(无条件),然后使用astro系统自带的paginate函数做分页即可,需要特别说明的是错误的路由命名会导致Paginate分页函数无法生成index.html的问题,所以一定需要使用[...page]这样的路由名称。
再说SSR模式,此模式下我们需要要做Api分页查询,SSR模式非常灵活和以往的服务端开发语言一样,该模式下可以使用astro的Astro.url.searchParams.get('...')函数获取到URL分页参数,也可以使用动态路由[...page]这样的命名方式来匹配有深度的URL如:/page/1.../page/2/...
需要注意的是Directus的query使用了多国语言字段 deep查询,以及meta=_filter_count参数返回查询总数量。
最后直接上SSR的代码实现,注意文件名为index.astro,生成出的的url格式为 /path/p?=1...
---
import Pagination from '../../components/Pagination.astro';
const currentPath = Astro.url.pathname;
const p = parseInt(Astro.url.searchParams.get('p')! || 1);
const lang = Astro.url.pathname.split('/')[1];
const pageSize = 2
const response = await fetch('https://xxx/items/articles?fields=*.*&deep[translations][_filter][languages_code][_eq]=' + lang + '&[children][_filter][status][_eq]=published&limit=' + pageSize + '&page=' + p + '&meta=filter_count').then((response) => response.json());
const count = parseInt(response.meta.filter_count)
const total = Math.ceil(count / pageSize);
const prevp = ( p - 1 > 0 ) ? currentPath + "?p=" + ( p - 1 ) : null
const nextp = ( p + 1 <= total ) ? currentPath + "?p=" + ( p + 1 ) : null
//pagination
const page = {
currentPage: p,
total: total,
size: pageSize,
url: {
prev: prevp,
next: nextp,
},
firstPage: currentPath,
lastPage: (p < total) ? currentPath + "?p=" + total : null,
count: count,
}
if( p > total){
return Astro.redirect(currentPath);
}
const data = response.data
---
<Layout title="BLOG">
<div class="text-sm breadcrumbs">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog">BLOG</a></li>
</ul>
</div>
<h1>BLOG</h1>
<ul>
{
data.map((article) => (
<Card href={`/blog/${article.slug}/`} title={article.translations[0].title} date_created={formatRelativeTime(new Date(article.date_created))} />
))
}
</ul>
<Pagination
firstPage={page.url.prev ? currentPath : null}
previousPage={page.url.prev ? page.url.prev : null}
nextPage={page.url.next ? page.url.next : null}
lastPage={page.lastPage ? page.lastPage : null}
currentPage={page.currentPage}
totalPages={page.total}
/>
</Layout>分页组件Pagination.astro
---
// Pagination Props type helpers
export type NumericalString = `${number}`
export type RouteString = string | null | undefined
interface Props {
firstPage?: RouteString
previousPage?: RouteString
nextPage?: RouteString
lastPage?: RouteString
currentPage?: string | number
totalPages?: NumericalString | number
}
const {
firstPage = '#',
previousPage = '#',
nextPage = '#',
lastPage = '#',
currentPage = '1',
totalPages = '12',
} = Astro.props
---
<nav class="pagination" aria-label="Pagination">
<ul class="pagination__list">
<li>
{
firstPage ? (
<a href={firstPage} aria-label="Go to the first page">
<svg
aria-hidden="true"
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M24.6667 9L18 15.6667L24.6667 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.6667 9L8 15.6667L14.6667 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</a>
) : (
<span class="disabled">
<svg
aria-hidden="true"
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M24.6667 9L18 15.6667L24.6667 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.6667 9L8 15.6667L14.6667 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
)
}
</li>
<li>
{
previousPage ? (
<a href={previousPage} aria-label={`Go back to ${previousPage}`}>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="32" height="32" viewBox="0 0 24 24">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m14 7-5 5 5 5"
/>
</svg>
</a>
) : (
<span class="disabled">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="32" height="32" viewBox="0 0 24 24">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m14 7-5 5 5 5"
/>
</svg>
</span>
)
}
</li>
<li>
<span>Page {currentPage} of {totalPages}</span>
</li>
<li>
{
nextPage ? (
<a href={nextPage} aria-label={`Go to ${nextPage}`}>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="32" height="32" viewBox="0 0 24 24">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m10 7 5 5-5 5"
/>
</svg>
</a>
) : (
<span class="disabled">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="32" height="32" viewBox="0 0 24 24">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m10 7 5 5-5 5"
/>
</svg>
</span>
)
}
</li>
<li>
{
lastPage ? (
<a href={lastPage} aria-label="Go to the last page">
<svg
aria-hidden="true"
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.33333 9L14 15.6667L7.33333 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M17.3333 9L24 15.6667L17.3333 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</a>
) : (
<span class="disabled">
<svg
aria-hidden="true"
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.33333 9L14 15.6667L7.33333 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M17.3333 9L24 15.6667L17.3333 22.3333"
stroke="currentColor"
stroke-width="2.66667"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
)
}
</li>
</ul>
</nav>
<style is:global>
.pagination .pagination__list {
display: flex;
align-items: center;
gap: 1rem;
}
.pagination a,
.pagination .disabled {
display: block;
border-width: 2px;
border-style: solid;
border-radius: 3px;
}
.pagination a {
border-color: currentColor;
transition: background-color 0.15s ease-in-out;
}
.pagination a:hover,
.pagination a:focus-visible {
background-color: orange;
}
.pagination a:focus-visible svg path {
stroke: #222;
}
.pagination .disabled {
border-color: grey;
opacity: 0.5;
}
</style>