博客搭建教程(3):前端页面
我们的页面代码用的是一个叫 JSX 的语法。它长这样: {post.title} 的意思是:把 JavaScript 变量 post.title 的值插入到 HTML 里。JSX = HTML + JavaScript,可以混着写。 在 Next.js App Router 里,页面文件默认是服务端组件(Server Component)。这意味着代码在服务器上运行,直接查数据库、渲染 HTML,...
博客搭建教程(3):前端页面 — 让文章显示在网页上
React 和 JSX 是什么?
我们的页面代码用的是一个叫 JSX 的语法。它长这样:
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
{post.title} 的意思是:把 JavaScript 变量 post.title 的值插入到 HTML 里。JSX = HTML + JavaScript,可以混着写。
服务端组件是什么?
在 Next.js App Router 里,页面文件默认是服务端组件(Server Component)。这意味着代码在服务器上运行,直接查数据库、渲染 HTML,然后把结果发给浏览器。用户看到的网页是完整的静态 HTML,加载极快。
服务端组件的标志:文件顶部没有 "use client" 这行。
第一步:写首页
项目默认生成的 src/app/page.tsx 我们可以重写。但为了更好的组织,我们新建 src/app/(public)/page.tsx((public) 是路由组,不影响 URL):
import { prisma } from "@/lib/prisma";
export default async function HomePage() {
// 从数据库查已发布的文章,按时间倒序
const posts = await prisma.post.findMany({
where: { published: true },
orderBy: { createdAt: "desc" },
});
return (
<div>
<h1>最新文章</h1>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content.slice(0, 200)}...</p>
</article>
))}
</div>
);
}
解释:
prisma.post.findMany()— 查多条数据where: { published: true }— 只查已发布的orderBy: { createdAt: "desc" }— 按创建时间倒序(新的在前)posts.map(...)— 遍历每篇文章,渲染成 HTMLkey={post.id}— React 要求列表每项有唯一 key
第二步:文章详情页
创建 src/app/(public)/articles/[slug]/page.tsx:
import { prisma } from "@/lib/prisma";
import { notFound } from "next/navigation";
export default async function ArticlePage({ params }: { params: { slug: string } }) {
const post = await prisma.post.findUnique({
where: { slug: params.slug },
});
if (!post) notFound(); // 404
// 简单的 Markdown 渲染(把 # 变标题)
const html = post.content
.replace(/^### (.+)$/gm, "<h3>$1</h3>")
.replace(/^## (.+)$/gm, "<h2>$1</h2>")
.replace(/^# (.+)$/gm, "<h1>$1</h1>")
.replace(/\n\n/g, "</p><p>")
.replace(/^(.+)$/gm, "<p>$1</p>");
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: html }} />
</article>
);
}
第三步:分页
文章多了需要分页。prisma.post.findMany() 接受 skip(跳过前N条)和 take(取N条):
const PAGE_SIZE = 10;
const page = 1;
const posts = await prisma.post.findMany({
where: { published: true },
orderBy: { createdAt: "desc" },
skip: (page - 1) * PAGE_SIZE,
take: PAGE_SIZE,
});
const total = await prisma.post.count({ where: { published: true } });
const totalPages = Math.ceil(total / PAGE_SIZE);
配套组件
把文章卡片、文章列表、分页按钮分别封装成组件,方便复用:
src/components/public/
├── ArticleCard.tsx # 单篇文章卡片
├── ArticleList.tsx # 文章列表
└── Pagination.tsx # 分页导航
小结
这篇我们:
- 理解了 React/JSX/服务端组件的概念
- 用 Prisma 查数据库并在网页显示
- 实现了文章列表和文章详情页
- 加上了分页功能
下一篇我们添加评论系统。