Astro SSR模式+Directus分页功能的实现

最近用Astro开发一个项目,高强度体验下来收获颇多,先说说SSR模式和SSG在模式分页下有何区别。

首先SSG模式,需要构造所有的静态路由,需要列遍数据,如果使用api做数据查询需要select出全部(无条件),然后使用astro系统自带的paginate函数做分页即可,需要特别说明的是错误的路由命名会导致,所以一定需要使用[...page]这样的路由名称。

再说SSR模式,此模式下我们需要要做Api分页查询,SSR模式非常灵活和以往的服务端开发语言一样,该模式下可以使用astroAstro.url.searchParams.get('...')函数获取到URL分页参数,也可以使用动态路由[...page]这样的命名方式来匹配有深度的URL如:/page/1.../page/2/...

需要注意的是Directusquery使用了多国语言字段 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>

Post Comment