import type { Metadata } from "next"; import { Link } from "@/i18n/navigation"; import { getTranslations, setRequestLocale } from "next-intl/server"; import { ArrowRight } from "lucide-react"; import { Container, Section } from "@/components/ui/layout"; import { SectionHeading } from "@/components/ui/section-heading"; import { buttonClassName } from "@/components/ui/button-variants"; import { PostCard } from "@/components/post/post-card"; import { MotionItem } from "@/components/motion/motion-item"; import { HomeHero } from "@/components/home/hero"; import { getAllPosts, getAllTags } from "@/lib/posts"; import { siteConfig } from "@/lib/site-config"; /** * 首页渲染策略:ISR 缓存 60 秒。 * * 内容来自数据库,因此不在构建期固化;访问时生成并缓存, * 更新文章后最多 60 秒自动生效。 * * 注意:Next.js 会静态分析路由段配置,这里必须写成**字面量**, * 不能引用从其他模块导入的常量,否则会报 * "Invalid segment configuration export detected"。 */ export const revalidate = 60; type HomePageProps = { params: Promise<{ locale: string }>; }; export async function generateMetadata({ params, }: HomePageProps): Promise { const { locale } = await params; const t = await getTranslations({ locale, namespace: "Site" }); return { title: `${siteConfig.name} · ${t("tagline")}`, description: siteConfig.description, alternates: { canonical: `/${locale}` }, }; } export default async function HomePage({ params }: HomePageProps) { const { locale } = await params; setRequestLocale(locale); const t = await getTranslations("Home"); // Hero 需要的统计数据量很小,直接在此等待; // 文章列表区块用 Suspense 包裹,先出骨架再填充内容。 const [posts, tags] = await Promise.all([getAllPosts(), getAllTags()]); const earliestPost = posts.at(-1); return ( <>
{t("latestTitle")}} description={t("latestDescription")} className="mb-xxl" /> {posts.length === 0 ? ( ) : (
{posts.map((post, index) => ( ))}
)}
{t("ctaSecondary")}
); } /** 无文章时的空状态(skill 要求前端具备加载态 / 空态 / 错误态) */ function EmptyState() { return (

还没有已发布的文章

调用{" "} POST /api/posts {" "} 创建文章(或运行{" "} pnpm seed {" "} 导入示例内容),状态为{" "} published {" "} 的文章就会出现在这里。

); }