老公,,我的老公!!老公你终于又发歌了,还是和🐑👄姐!
细心的读者已经发现了,今天 Jim 把欠了一年多的主页分页给做了。说实话,现在博客面临一个比较严峻的问题——要不要迁移到重大更新 Next.js 13 上,但是本着“能不改就不改”的原则,Jim 最终就没弄了,问题是在 Next.js 12 中,分页特别麻烦,尤其是需要处理 staticProps
的时候,所以这次的开发也是以规避直接触碰 staticProps
为方法核心。
本文的想法来源于 LogRocket 的这篇文章,简言之,使用了 react.js
中的 useState
hook 来实现分页。
基本思路
首先,我们需要知道,我们的分页是基于什么的。在这里,我们的分页是基于 posts
的,所以我们需要知道 posts
的总数,那很容易,通过 fs
来查一下 posts
文件夹下的文件数量,通过 getStaticProps
传递给预渲染就行了。
好,到预渲染环节,我们用个简单的 useState
就能解决当前页面的切换问题,这样就可以实现分页了。
对用户显示的包含分页的页面
// pages/index.js
import Pagination from '../components/pagination'
import { useState } from 'react'
export default function Home() {
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 10; // Let default num be 10.
const onPageChange = (page) => {
setCurrentPage(page);
};
return (
<Pagination
items={count}
currentPage={currentPage}
pageSize={pageSize}
onPageChange={onPageChange}
/>
)
}
export async function getStaticProps({ locale }) {
// Get files from the posts dir
const files = fs.readdirSync(path.join('posts'))
// Get slug and frontmatter from posts
const posts = files.map((filename) => {
const slug = filename.replace('.md', '')
const markdown = fs.readFileSync(
path.join('posts', filename),
'utf-8'
)
return { slug }
})
// return total amount of posts
const total = posts.length
return {
props: {
posts: posts.sort(sortByDate).sort(pin),
count: total,
},
}
}
分页组件
// components/pagination.js
export default function Pagination({ items, pageSize, currentPage, onPageChange }) {
const pagesCount = Math.ceil(items / pageSize)
if (pagesCount === 1) return null
const pages = Array.from({ length: pagesCount }, (_, i) => i + 1)
return (
<div className='pagination-container'>
<ul className='pagination'>
{(() => {
if (currentPage !== 1) {
return (
<li
className='pageItem'
onClick={() => onPageChange(currentPage - 1)}
>
<
</li>
)
}
})()}
{pages.map((page) => (
<li
key={page}
className={
page === currentPage ? 'pageItemActive' : 'pageItem'
}
onClick={() => onPageChange(page)}
>
{page}
</li>
))}
{(() => {
if (currentPage !== (parseInt(pages) + 1)) {
return (
<li
className='pageItem'
onClick={() => onPageChange(currentPage + 1)}
>
>
</li>
)
}
})()}
</ul>
</div>
)
}
裁剪 posts
这里我们不如写个小脚本:
// utils/index.js
export const paginate = (items, pageNumber, pageSize) => {
const startIndex = (pageNumber - 1) * pageSize;
return items.slice(startIndex, startIndex + pageSize);
};
然后回到 index.js
中,再把其余组件凑齐:
// pages/index.js
import { paginate } from '../utils'
import Head from '../components/head'
import Post from '../components/post'
import Header from '../components/header'
import Footer from '../components/footer'
import Pagination from '../components/pagination'
export default function Home({ posts, count }) {
const router = useRouter()
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 8;
const onPageChange = (page) => {
setCurrentPage(page);
};
return (
<>
<Head title='' />
<Header />
<div className='main'>
<section className='posts'>
{paginate(posts, currentPage, pageSize).filter(post => post.frontmatter.locale === router.locale).map((post, index) => (
<Post key={index} post={post} default_locale={router.defaultLocale} />
))}
</section>
</div>
<Pagination
items={count}
currentPage={currentPage}
pageSize={pageSize}
onPageChange={onPageChange}
/>
<Footer />
</>
)
}