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>