skip to content
鰭狀漏斗

Astro 分頁函式

/ 閱讀時間 4 分鐘

分頁(pagination)就是把一些資料每幾筆資料分成一組放在同一頁,產生若干頁的頁面。比方說現在有 100 筆資料,每頁放 7 筆資料,經過分頁就會產生 15 個頁面,在最後的第 15 頁只會有最後的 2 筆資料。

Astro 的 paginate() 函式

Astro 有內建 paginate() 函式可以做到分頁功能。下面就用稍微修改過的官方文件的例子來說明這個函式:

src/pages/astronauts/[...page].astro
---
export async function getStaticPaths({ paginate }) {
const astronauts = [{
astronaut: 'Neil Armstrong',
}, {
astronaut: 'Buzz Aldrin',
}, {
astronaut: 'Sally Ride',
}, {
astronaut: 'John Glenn',
}];
// Generate pages from our array of astronauts, with 1 to a page
return paginate(astronauts, { pageSize: 1 });
}
// All paginated data is passed on the "page" prop
const { page } = Astro.props;
---
<!--Display the current page number. Astro.params.page can also be used!-->
<h1>Page {page.currentPage}</h1>
<ul>
<!--List the array of astronaut info-->
{page.data.map(({ astronaut }) => <li>{astronaut}</li>)}
</ul>

這個檔案會將 astronauts 陣列做分頁,產生的頁面會在 /astronauts(第一頁)、/astronauts/2/astronauts/3 …以此類推。(如果是原版官方文件的例子,第一頁會在 /astronauts/1,差別在於檔名不同。)

paginate() 函式不需要另外引入,只要在 getStaticPaths() 做為參數引入就可以使用。如果在這個函式裡回傳 paginate() 的回傳值,產生的頁面會有 page prop 可以使用。這個 prop 除了分頁後的資料 page.data 外,還有像上/下一頁的連結(page.url.prev / page.url.next)之類有用的屬性。

paginate() 的不足與暫時解法

不過我覺得這樣的 url 可能造成誤解。拿上面的例子來說,/astronauts/2 可能被誤解成二號太空人,而不是太空人清單的第二頁。如果把這個檔案移到 src/pages/astronauts/page/[...page].astro,雖然第二頁 /astronauts/page/2 就不會造成誤解了,但是第一頁的 /astronauts/page 又會覺得很奇怪,我會想要它在原來的 /astronauts

要達成這個目標,除了自己寫分頁的邏輯以外,也可以在有用 paginate() 的程式碼做一些更改。想法大致上是把第一頁跟其他頁分開,第一頁自己做分頁,不使用 paginate(),其他頁照樣使用 paginate(),但是要把 paginate() 產生的第一頁去掉,還要把第二頁往上一頁的連結替換成我們自己寫的第一頁連結。

第一頁在 src/pages/astronauts/index.astro,不使用 dynamic routing,所以也不會有 getStaticPaths()。在這一頁自己拿這一頁會有的資料做 render,另外自己設定往第二頁的連結(如果有第二頁的話)。

其他頁在 src/pages/astronauts/page/[...page].astro,一樣有 paginate(),但是要把第一頁去掉,所以要改成:

return paginate(astronauts, { pageSize: 1 }).slice(1);

然後還要判斷如果是在第二頁的話,不能用 page prop 的 page.url.prev,必須自己設定往第一頁的連結。

沒被採納的提議

在 Astro roadmap 有人提出讓 paginate() 可以首頁跟其他頁路徑分開的建議(這個這個),可惜除了我以外似乎沒有人理的樣子,更不用說進入後續階段了。現在如果想要這樣做的話,除了自己寫外,就只能做上述的 workaround 了。