ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Next.js × Cosmic 静态博客:从 getStaticPaths 到 Draft Mode 预览的完整实现解析

Next.js × Cosmic 静态博客:从 getStaticPaths 到 Draft Mode 预览的完整实现解析 Next.js × Cosmic 静态博客从 getStaticPaths 到 Draft Mode 预览的完整实现解析【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js本篇基于 Next.js 官方示例仓库中的 cms-cosmic 示例 展开讲解如何以 Cosmic 作为 Headless CMS 数据源利用 Next.js 的静态生成Static Generation构建一个博客站点。读完本文你将完整掌握该示例的搭建步骤、三个环境变量与 Cosmic API 的对应关系、getStaticPaths的 fallback 机制、以及从/api/preview到setDraftMode的草稿预览Preview/Draft Mode全链路实现原理。一、示例定位静态生成博客 Headless CMScms-cosmic 是 Next.js 仓库examples/目录下的一组 CMS 集成示例之一。它的核心定位是展示如何用 Next.js 的静态生成功能从 Cosmic一种 Hosted Headless CMS拉取文章数据在构建期/请求期渲染为静态页面并支持 CMS 后台的草稿预览。与示例配套的源码结构如下pages/index.tsx首页通过getStaticProps拉取全部文章pages/posts/[slug].tsx文章详情页通过getStaticPathsgetStaticProps实现动态路径的静态生成pages/api/preview.ts、pages/api/exit-preview.ts预览模式开启/关闭的 API 路由lib/api.tsx封装 Cosmic SDK 的数据获取函数是所有数据请求的唯一出口lib/markdownToHtml.tsMarkdown 到 HTML 的转换interfaces/index.tsPostType等 TypeScript 类型定义components/布局与文章展示组件预览提示条、文章头、正文等next.config.jsNext.js 配置重点是图片域名白名单。整个示例的依赖非常精简从 package.json 可以看到数据层依赖cosmicjsCosmic 官方 SDKMarkdown 渲染依赖remarkremark-html日期处理用date-fns样式用 Tailwind CSS。二、快速启动示例仓库 README 给出三种包管理器方式使用create-next-app引导式创建示例项目# npm npx create-next-app --example cms-cosmic cms-cosmic-app# Yarn yarn create next-app --example cms-cosmic cms-cosmic-app# pnpm pnpm create next-app --example cms-cosmic cms-cosmic-app执行后会得到一个名为cms-cosmic-app的独立项目其中已包含示例目录下的全部源码与.env.local.example模板文件。三、配置 Cosmic 账户与三个环境变量Step 1创建账户并安装 App先在 Cosmic 官网注册账户然后从 Cosmic App Marketplace 安装Next.js Static Blog应用。这个步骤会引导你在 Cosmic 后台创建好 Bucket、posts 数据模型title、slug、excerpt、cover_image、author、content 等字段是后续所有 API 调用能命中数据的前提。Step 2准备.env.local进入 Cosmic 后台的Settings Basic Settings然后复制示例目录中的环境变量模板cp .env.local.example .env.local模板文件.env.local.example内容只有三行COSMIC_BUCKET_SLUG COSMIC_READ_KEY COSMIC_PREVIEW_SECRET在.env.local中填入实际值COSMIC_BUCKET_SLUGCosmic 后台API Access区域的Bucket slugCOSMIC_READ_KEYAPI Access区域的Read Key只读密钥足够本示例的读场景使用;COSMIC_PREVIEW_SECRET任意随机字符串避免空格用于预览模式的身份校验。这三个变量与源码的对应关系可以从 lib/api.tsx 得到印证const BUCKET_SLUG process.env.COSMIC_BUCKET_SLUG; const READ_KEY process.env.COSMIC_READ_KEY; const bucket Cosmic().bucket({ slug: BUCKET_SLUG, read_key: READ_KEY, });而COSMIC_PREVIEW_SECRET只在 pages/api/preview.ts 中参与校验见下文预览模式一节。Step 3启动开发模式npm install npm run dev # 或 yarn install yarn dev启动后博客运行在http://localhost:3000。四、静态生成链路getStaticPaths 与 getStaticProps首页全量文章拉取pages/index.tsx 的getStaticProps调用 lib/api.tsx 中的getAllPostsForHomeexport const getAllPostsForHome async (preview: boolean): PromisePostType[] { const params { query: { type: posts, }, props: title,slug,metadata,created_at, sort: -created_at, ...(preview { status: any }), }; const data await bucket.getObjects(params); return data.objects; };注意几个 Cosmic API 的用法细节query: { type: posts }限定只查 posts 类型对象props字段白名单让响应只携带渲染首页所需的字段减少传输量sort: -created_at按创建时间倒序首页据此把第一篇作为 Hero 大图文章、其余进入更多文章列表见 index.tsx 中allPosts[0]与allPosts.slice(1)的切分...(preview { status: any })是预览模式的关键开关只有处于预览态时才向 Cosmic 追加status: any从而把草稿draft状态的文章也一并返回正常构建时只会拿到已发布published内容。文章页动态路径 fallbackpages/posts/[slug].tsx 中的getStaticPaths在构建期向 Cosmic 查询全部文章 slugexport async function getStaticPaths() { const allPosts (await getAllPostsWithSlug()) || []; return { paths: allPosts.map((post) /posts/${post.slug}), fallback: true, }; }getAllPostsWithSlug只请求props: slug见 lib/api.tsx是最轻量的查询fallback: true意味着构建期未包含的 slug 不会直接 404而是在首次访问时触发getStaticProps按需生成并缓存——这对CMS 里随时可能新增文章的场景非常关键。页面组件里对应的兜底逻辑是const router useRouter(); if (!router.isFallback !post?.slug) { return ErrorPage statusCode{404} /; }即 fallback 期间显示 Loading… 占位标题若最终拿不到文章则渲染 404见 [slug].tsx。getStaticProps侧调用getPostAndMorePosts(slug, preview)一次拿到当前文章 最多 2 篇相关文章Cosmic 查询limit: 3再过滤掉当前 slug 后slice(0, 2)见 lib/api.tsx并把metadata.content中的 Markdown 在服务端转成 HTMLconst content await markdownToHtml(data[post]?.metadata?.content || );Markdown 渲染lib/markdownToHtml.ts 的完整实现只有 7 行import { remark } from remark; import html from remark-html; const markdownToHtml async (markdown: string) { const result await remark().use(html).process(markdown); return result.toString(); }; export default markdownToHtml;由于转换发生在getStaticProps中客户端收到的已经是最终 HTML浏览器端无需任何 Markdown 解析开销。文章数据结构由 interfaces/index.ts 定义PostType包含title、slug、content、created_at以及metadata封面图cover_image、作者author、摘要excerpt其中图片类型为ImgixType同时保存url与imgix_url为下一节的图片处理做准备。五、Preview ModeDraft Mode从 CMS 后台一键预览草稿这是本示例最有实战价值、也最能体现 Next.js 静态生成与 CMS 配合技巧的部分。原文档 Step 5 的操作流程与源码实现一一对应1. 在 Cosmic 后台配置 Preview Link进入Posts Edit Settings在 Preview Link 区域填入README 原图展示了 Cosmic 后台该区域的截图位置http://localhost:3000/api/preview?secretsecretslug[object_slug]secret即你在.env.local中设置的COSMIC_PREVIEW_SECRET[object_slug]是 Cosmic 的 shortcode点击时会被自动替换为该文章的slug字段值。2./api/preview的安全校验与开启pages/api/preview.ts 的完整逻辑值得逐段对照阅读export default async function preview(req, res) { // 校验 secret 与 slug 参数secret 只应被本路由和 CMS 知道 if ( req.query.secret ! process.env.COSMIC_PREVIEW_SECRET || !req.query.slug ) { return res.status(401).json({ message: Invalid token }); } // 回查 CMS确认该 slug 真实存在 const post await getPreviewPostBySlug(req.query.slug); if (!post) { return res.status(401).json({ message: Invalid slug }); } // 通过设置 Cookie 开启 Draft Mode res.setDraftMode({ enable: true }); // 重定向到从 CMS 取回的 slug 对应路径 // 注意不使用 req.query.slug 重定向避免开放重定向open redirect漏洞 res.writeHead(307, { Location: /posts/${post.slug} }); res.end(); }三个要点双重校验先比对COSMIC_PREVIEW_SECRET再调用getPreviewPostBySlug回查 Cosmic。该函数lib/api.tsx携带status: any查询即草稿也能查到——这正是COSMIC 里有草稿但页面没发布时预览依然生效的原因res.setDraftMode({ enable: true })示例采用的是 Next.js 新版 Draft Mode API通过设置专用 Cookie 开启而非旧版setPreviewData。开启后该浏览器的请求命中getStaticProps时会自动带上preview: true参数——这正是前面getAllPostsForHome(preview)、getPostAndMorePosts(slug, preview)签名中preview参数的来源它触发status: any让草稿数据流入静态渲染防开放重定向代码注释明确说明重定向目标不是用户传入的req.query.slug而是从 CMS 回查结果里取回的post.slug避免构造恶意slug把用户重定向到外部站点。3. 草稿态的完整体验按 README 描述的实验流程把某篇文章标题改成带[Draft]前缀只点Save Draft而不点 Publish。此时直接访问该文章页 → 看到旧标题静态页面仍是已发布版本点击 Cosmic 后台的Preview Link按钮 → 走/api/preview开启 Draft Mode 后 307 重定向到/posts/[slug]→ 看到带[Draft]的新标题。4. 退出预览页面顶部的提示条由 components/alert.tsx 渲染处于预览态时显示 This page is a preview. Click here to exit preview mode.链接指向/api/exit-preview。该路由pages/api/exit-preview.ts实现极简export default function exit(_, res) { // 通过移除 Cookie 退出 Draft Mode res.setDraftMode({ enable: false }); res.writeHead(307, { Location: / }); res.end(); }六、图片配置Cosmic 的 Imgix 域名白名单Cosmic 的图片托管基于 Imgix文章的imgix_url指向imgix.cosmicjs.com。为了让Image组件合法加载这些远程图next.config.js 配置了remotePatternsmodule.exports { images: { remotePatterns: [ { protocol: https, hostname: imgix.cosmicjs.com, port: , pathname: /my-account/**, }, ], }, };从源码结构看pathname前缀/my-account/**是 Cosmic Bucket 图片 URL 的公共前缀my-account为占位命名实际 URL 以各自 Bucket 为准。配置时若域名不匹配next/image会在开发期报 doesnt match any configured remotepatterns 错误对应仓库错误文档 next-image-unconfigured-localpatterns.mdx。七、部署本地开发验证通过后按 README 的 Step 6可将项目推送到 Git 仓库再导入 Vercel 部署。部署时的关键动作是在 Vercel 的Environment Variables中配置与.env.local相同的三个变量COSMIC_BUCKET_SLUG、COSMIC_READ_KEY、COSMIC_PREVIEW_SECRET否则构建期拉取数据会因缺少凭证而失败。README 同时提供了基于官方模板的一键 Deploy 入口其中预设了同样这三个环境变量的名称。八、小结与延伸阅读cms-cosmic 示例的价值在于用最小的代码量串起了 SSG 博客的三个工程要点构建期数据预取getStaticPathsfallback: true、按字段白名单 status: any精细控制 CMS 查询、以及secret 校验 setDraftMode 安全重定向组成的草稿预览闭环。这套模式同样适用于接入其他 Headless CMS。仓库中还有大量结构相近的 CMS 集成示例可作对照参考例如Contentful、Sanity、Prismic、WordPress、Payload、Tina以及不带 CMS 的基础博客模板 Blog Starter。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表