视图层升级:从 PHP 到 Next.js
作为博客站点,从 SEO 等角度,HTML 代码预生成的能力是必须的。由于成本考虑,博客一直使用 PHP 虚拟主机,所以一直使用了 PHP 的服务端渲染。
但是 PHP 视图层的开发体验,与现有的前端生态较难接轨,开发效率不佳。在了解 Next.js 时,看到了静态页面生成 SSG (Static Site Generation) 的方案,这让我眼前一亮。
用了几个小时的试验,成功将视图层从 PHP 升级到了 Next.js。
实现博客列表页、详情页
使用 npx create-next-app@latest
快速创建了 ts 的项目模板,结合 Pre-rendering and Data Fetching官方文档教程,很快的将博客列表页、详情页渲染了出来。
比如,对于博客的列表页,创建 /posts/${page}
规则的路由。这里为了减少 DOM 渲染,还是保留了分页逻辑。
在 getStaticPaths
中,计算一共需要生成多少个“列表页”。先拉去总共的文章数量,每 20 篇文章生成一个列表页。
interface Params extends ParsedUrlQuery {
page: string;
}
export const getStaticPaths: GetStaticPaths<Params> = async () => {
let { totalPage } = await getPostList({ page: 1, count: 20 });
return {
paths: Array.from({ length: totalPage }).map((_, index) => {
return {
params: { page: (index + 1).toString() },
};
}),
fallback: false,
};
};
在 getStaticProps
中,为每个页面拉取数据,比如第 5 页,就会去接口拿第五页的 20 篇文章进行渲染。
export const getStaticProps: GetStaticProps<PostListProps, Params> = async (context) => {
const { page } = context.params! as Params;
let { posts, current, pageSize, totalCount } = await getPostList({
page: Number(page) || 1,
count: 20,
});
return {
props: {posts, current, pageSize, totalCount,},
};
};
通过这样的数据获取,在构建时,页面组件会接受入参,进行渲染和处理即可。
export default function PostDataListPage({ posts, current, pageSize, totalCount }: PostListProps) {
return <div> ... </div>;
}
实现搜索页面
对于动态搜索,也是必不可少的。 SSG 方案的妙处在于,它会将入参数据生成为 json 文件,能作为动态数据处理。
在文章列表页的基础上,提取了文章列表、分页等样式作为文章列表样式组件,在原有的 pages 下只做数据处理,并创建 /pages/search
页面用于处理搜索:
- 入参 posts 代表全部文章数据,这个会在构建时存储到页面中。
- 使用 data 表示过滤后的数据,使用 page 表示当前的页码,通过 slice 计算出当前页应当渲染的 20 篇文章。
- 使用 qeury.v 来表示用户输入的过滤文本,结合路由 query 参数的改变,更新过滤页面应该展示的 data 数据。
const router = useRouter();
const [data, setData] = useState<Post[]>([]);
const [page, setPage] = useState(1);
const getData = () => {
let val = router.query.v.toLocaleLowerCase();
setData(
posts.filter((item) => {
return (
item.title.toLocaleLowerCase().includes(val) ||
item.remark.toLocaleLowerCase().includes(val) ||
item.content.includes(val)
);
}),
);
setPage(1);
};
useEffect(() => {
getData();
}, [router.query.v, posts]);
const limitNum = (page - 1) * 20;
const viewPosts = data.slice(limitNum, limitNum + 20);
在文章数据不多的情况下,这个搜索方案也可以获得较快的速度。
升级效果
本次视图层的升级,开发体验和站点性能都得到了较大提升。
开发体验:
- 甩开视图层的 PHP ,接口服务变得更加纯粹,只需专注于后端业务逻辑。
- Next.js 让廉价的虚拟主机能够保持 SEO 能力的同时,轻易的用上 React、TypeScript 等主流的前端技术。
- 纯静态一直以来是 PHP 站点的重要优化方案,但会增加了较大复杂度,而在 Next.js 中使用很简单的前端代码,就能够完成这一切。
站点性能:
- 比起传统的纯静态方案,Next.js 的 SSG 更具备了预渲染的能力。
- 并且,它在进入下一个路由时,不是直接跳转页面,而是作为单页应用,去加载 json 数据,直接渲染。
- 这使得页面之间的切换变得极快,无需访问数据库,也无需加载额外的前端页面及静态资源。
由于构建是在本地进行,所以本地始终会保留一份最新的静态站点和 JSON 数据,也算是一种数据备份。
Next.js 升级到 app 路由
在 博客升级到 Next.js 14 ,迁移到 app 路由,升级 SSG 模式 文章 SSG 升级 中说明。