发布时间:2026/9/7 18:45:36
Next.js × DatoCMS:用静态生成与 Preview Mode 构建 Headless CMS 博客的完整实践 Next.js × DatoCMS用静态生成与 Preview Mode 构建 Headless CMS 博客的完整实践【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js本文基于 Next.js 仓库中的examples/cms-datocms示例讲解如何以 DatoCMS 作为数据源通过getStaticPaths/getStaticProps实现一个静态生成的博客站点并结合 Draft Mode预览模式在内容发布前实时预览草稿。读完后你将完整掌握从 DatoCMS 建模、环境变量配置、数据获取层设计到本地预览与部署的全流程并能读懂示例中每一处关键源码的实现意图。示例项目概述该示例展示的是 Next.js 的**静态生成Static Generation**能力文章数据来自 DatoCMS 的 GraphQL API在构建/请求时预取并生成静态页面而草稿内容则通过 Preview Mode 绕过静态产物直接实时渲染。示例采用 Pages Router 技术栈核心文件组织如下相对仓库根目录文件职责pages/index.js首页getStaticProps拉取最新文章列表pages/posts/[slug].js文章详情页getStaticPaths预生成所有文章路径pages/api/preview.js启用 Draft Mode 的 API 路由pages/api/exit-preview.js退出 Draft Mode 的 API 路由lib/api.jsDatoCMS GraphQL 数据获取层4 个查询函数lib/markdownToHtml.js将正文 Markdown 转成 HTMLlib/constants.js站点常量如 CMS 名称next.config.js配置next/image允许加载 DatoCMS 图片域名components/展示层组件文章头部、封面、正文、预览提示条等依赖方面package.json 中的关键运行时依赖为react-datocms渲染 DatoCMS 图片组件、remarkremark-htmlMarkdown 转 HTML、date-fns日期格式化、classnames样式侧使用tailwindcss、postcss、autoprefixer。快速开始用 create-next-app 引导项目执行create-next-app并指定cms-datocms示例即可引导完整项目支持 npm、Yarn 与 pnpm 三种方式npx create-next-app --example cms-datocms cms-datocms-appyarn create next-app --example cms-datocms cms-datocms-apppnpm create next-app --example cms-datocms cms-datocms-app引导完成后项目目录中会包含示例源码以及一个.env.local.example环境变量模板文件见 examples/cms-datocms/.env.local.example其中只定义了两个待填写的变量DATOCMS_API_TOKEN DATOCMS_PREVIEW_SECRETDatoCMS 端配置项目与模型设计第 1 步创建 DatoCMS 账号与项目在 DatoCMS 上注册账号后从 Dashboard 创建一个新项目New project可以选择Blank Project空白项目。第 2 步创建Author作者模型在项目的设置页新建一个Model命名为Author然后添加以下字段无需修改默认设置字段名字段类型NameText字段Single-line StringPictureMedia字段Single asset第 3 步创建Post文章模型同样新建一个Model名称为Post。重点在 Additional Settings附加设置页签中打开Enable draft/published system启用草稿/发布系统。这是后续预览模式能区分草稿与已发布内容的前提。字段清单如下除特别注明外不必修改设置字段名字段类型额外配置TitleTextSingle-line String—ContentTextMultiple-paragraph Text支持 Markdown—ExcerptTextSingle-line String—Cover ImageMediaSingle asset—DateDate and timeDate—AuthorLinksSingle link在 Validations 页签的 Accept only specified model 中选择AuthorSlugSEOSlug在 Validations 页签的 Reference field 中选择Title其中Slug字段引用Title自动生成正好对应 Next.js 端pages/posts/[slug].js的动态路由参数Author的单链接校验保证了文章只能关联作者模型与代码中author { name picture }的 GraphQL 查询结构一一对应。第 4 步录入内容在顶部Content菜单中选择Author创建一条新记录——只需要1 条即可文字可用占位数据头像可从 Unsplash 下载。选择Post创建新记录建议至少创建2 条文章Content字段支持写 Markdown封面图可从 Unsplash 下载并选择之前创建的Author。重要每条 Post 记录保存后必须点击Publish否则文章会停留在草稿draft状态不会出现在正式内容中。环境变量配置在 DatoCMS 顶部Settings菜单中点击API tokens然后复制Read-only API token只读 API token。将示例目录中的.env.local.example复制为.env.local该文件会被 Git 忽略cp .env.local.example .env.local然后在.env.local中填写两个变量变量取值说明用途DATOCMS_API_TOKEN上一步复制的只读 API token携带为Authorization: Bearer token请求 DatoCMS GraphQL APIDATOCMS_PREVIEW_SECRET任意随机字符串避免空格如MY_SECRET作为开启 Preview Mode 的身份校验密钥填写后的.env.local形如DATOCMS_API_TOKEN... DATOCMS_PREVIEW_SECRET...本地运行与预览模式第 6 步以开发模式运行 Next.jsnpm install npm run dev # 或 yarn install yarn dev博客运行在http://localhost:3000。第 7 步体验预览模式Preview Mode在 DatoCMS 中打开一篇文章修改标题例如在标题前加[Draft]点击Save但不要点击Publish此时该文章处于草稿状态。直接访问文章页不会看到新标题静态页仍是旧内容但通过Preview Mode即可看到变更。若文章没有变成草稿需回到Post模型设置的Additional Settings中确认已打开Enable draft/published system。访问以下 URL 开启预览http://localhost:3000/api/preview?secretsecretslugslugsecret填入DATOCMS_PREVIEW_SECRET对应的字符串slug文章的slug属性值可在 DatoCMS 中查看。开启后页面顶部会出现 Click here to exit preview mode 提示条点击即可退出预览对应pages/api/exit-preview.js。第 8 步部署可将项目推送到代码托管平台后导入 Vercel 进行部署。注意在 Vercel 项目设置中进入Environment Variables将DATOCMS_API_TOKEN与DATOCMS_PREVIEW_SECRET设置为与.env.local一致的值也可以使用本仓库该示例对应的 Vercel 模板一键部署部署时会要求填写上述两个环境变量。源码纵览DatoCMS 数据获取层lib/api.jslib/api.js 是示例的数据核心所有页面查询都收敛到这一层。通用请求函数与预览端点切换fetchAPI是所有 GraphQL 查询的统一入口lib/api.js#L21-L40const API_URL https://graphql.datocms.com; const API_TOKEN process.env.DATOCMS_API_TOKEN; async function fetchAPI(query, { variables, preview } {}) { const res await fetch(API_URL (preview ? /preview : ), { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${API_TOKEN}, }, body: JSON.stringify({ query, variables }), }); const json await res.json(); if (json.errors) { console.error(json.errors); throw new Error(Failed to fetch API); } return json.data; }两个值得注意的实现细节预览端点切换当preview为true时请求地址从https://graphql.datocms.com变为https://graphql.datocms.com/preview。DatoCMS 的/preview端点会返回含草稿的内容这正是预览模式能看到未发布文章的根本原因。错误处理一旦响应中包含errors立即打印并抛出Failed to fetch API让构建或 SSR 阶段尽早失败而不是渲染出残缺页面。响应式图片 Fragment示例定义了一个 GraphQL fragment用来一次性取齐 DatoCMS 响应式图片的全部信息lib/api.js#L5-L19const responsiveImageFragment fragment responsiveImageFragment on ResponsiveImage { srcSet webpSrcSet sizes src width height aspectRatio alt title bgColor base64 } ;其中webpSrcSet用于提供 WebP 变体base64用于渲染占位LQIP效果。封面图查询时通过responsiveImage(imgixParams: {fm: jpg, fit: crop, w: 2000, h: 1000})指定 imgix 图片处理参数——指定格式、裁剪方式与输出尺寸由 DatoCMS 的图片 CDN 按需产出不同分辨率的srcSet。四个查询函数函数作用关键查询点getAllPostsWithSlug构建期收集所有文章 slugallPosts { slug }供getStaticPaths生成路径列表getAllPostsForHome(preview)首页文章列表allPosts(orderBy: date_DESC, first: 20)含封面响应式图与作者信息头像裁剪为 100×100 并sat: -100去除饱和度getPostAndMorePosts(slug, preview)文章详情 推荐文章按filter: {slug: {eq: $slug}}取单篇另附morePosts: allPosts(first: 2, filter: {slug: {neq: $slug}})取两篇最新文章作延伸阅读getPreviewPostBySlug(slug)预览校验用固定preview: true只取slug用于确认该 slug 在 CMS 中真实存在源码纵览静态生成与 Draft Mode文章详情页getStaticPaths fallbackpages/posts/[slug].js 展示了完整的静态生成模式export async function getStaticProps({ params, preview false }) { const data await getPostAndMorePosts(params.slug, preview); const content await markdownToHtml(data?.post?.content || ); return { props: { preview, post: { ...data?.post, content }, morePosts: data?.morePosts ?? [], }, }; } export async function getStaticPaths() { const allPosts await getAllPostsWithSlug(); return { paths: allPosts?.map((post) /posts/${post.slug}) || [], fallback: true, }; }getStaticPaths通过getAllPostsWithSlug()在构建期枚举全部文章生成/posts/slug路径列表fallback: true表示遇到未预生成的 slug 时先渲染骨架组件中router.isFallback为真时显示 Loading…再按需生成该页的静态资源。getStaticProps的第二个参数preview由 Next.js 根据当前请求是否处于 Draft Mode 自动注入处于预览模式时它等价于true于是getPostAndMorePosts走/preview端点取草稿数据正文 Markdown 在此处经 lib/markdownToHtml.js基于 remark 体系转为 HTML 后作为 prop 传入页面。若 slug 查不到文章!post?.slug且非 fallback 状态页面渲染next/error的 404 页。首页 pages/index.js 更简洁getStaticProps({ preview false })调用getAllPostsForHome(preview)取第一条作 Hero 文章、其余进入 More Stories 列表。预览 API校验、开启与退出pages/api/preview.js 是 Draft Mode 的入口其防御逻辑值得逐段学习pages/api/preview.js#L4-L27双重校验req.query.secret必须与process.env.DATOCMS_PREVIEW_SECRET完全一致且必须携带slug任一不满足即返回401 Invalid token。slug 真实性校验调用getPreviewPostBySlug(req.query.slug)走 DatoCMS/preview端点确认该 slug 确实存在否则返回401 Invalid slug——防止任意 slug 换取预览 Cookie。开启 Draft Moderes.setDraftMode({ enable: true })通过写入 Cookie 标记后续请求处于草稿模式从而让getStaticProps的preview参数变为true。安全重定向使用307跳转到/posts/${post.slug}并且注释明确指出不直接重定向到req.query.slug以避免开放重定向open redirect漏洞。退出预览由 pages/api/exit-preview.js 完成res.setDraftMode({ enable: false })移除 Cookie 后同样以307跳回首页。页面顶部 Click here to exit preview mode 提示条即指向该路由。其他工程配置要点图片域名白名单next.config.js 通过images.remotePatterns放行https://www.datocms-assets.com/my-account/**这是next/image加载 DatoCMS 图片 CDN 资源的必要前提module.exports { images: { remotePatterns: [ { protocol: https, hostname: www.datocms-assets.com, port: , pathname: /my-account/**, }, ], }, };路径别名jsconfig.json 配置了/*指向项目根目录源码中因此可以写import { getAllPostsForHome } from /lib/api。构建脚本package.json 仅提供devnext、buildnext build、startnext start三个脚本静态生成的产物由next build在构建期产出。相关 CMS 示例Next.js 仓库为众多 headless CMS 提供了同构的博客示例数据获取层、静态生成与预览模式结构基本一致可横向对比 GraphQL/REST 接法差异相对仓库根目录的路径如下AgilityCMSBuilder.ioButterCMSContentfulCosmicDatoCMSDotCMSDrupalEnterspeedGhostGraphCMSKontent.aiMakeSwiftPayloadPlasmicPreprPrismicSanitySitecore XM CloudSitefinityStoryblokTakeShapeTinaUmbracoUmbraco heartcoreWebinyWordPressBlog Starter无 CMS 的版本小结cms-datocms示例以最小化的代码量完整演示了 headless CMS 静态生成的标准范式DatoCMS 侧通过模型与草稿/发布系统管理内容Next.js 侧用getStaticPaths/getStaticProps在构建期预生成页面用/preview端点与 Draft Mode Cookie 打通编辑中内容的实时预览并以 secret slug 双重校验、307 安全重定向保证预览入口不被滥用。这套模式可以直接迁移到 Contentful、Sanity、Prismic 等其他 CMS 示例中。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

2026/9/7 19:40:46

MySQL事务提交失败处理实战:回滚、重试与幂等设计

在开发中遇到“MySQL事务提交失败”这类问题,几乎是每个后端工程师都绕不过去的坎。尤其是涉及订单、库存、支付这类核心链路时,一旦事务在提交阶段爆出异常,很多人第一反应就是“回滚不就完了”,但真正落地时却发现,情…

2026/9/7 19:40:46

数据库课程为何从C++热身开始?CMU 15-445 Project #0解析

1. 为什么一门数据库课程要把第一个项目做成C热身很多人第一次看到CMU 15-445的Project #0时都有同一个疑问:我明明是来学数据库的,为什么第一个任务不是写SQL解析器,也不是实现存储引擎,而是先做一堆C练习?这个问题想…

2026/9/7 19:35:46

ETL设计实战:从分层架构到增量同步与性能优化

做数据集成这行越久,我越觉得 ETL 不只是一门“技术活”,它更像是在给企业数据大厦浇筑地基。不管是传统数仓还是现在的数据湖仓一体,数据要能真正用起来,第一步永远绕不开抽取、转换、加载这几个动作。很多刚入门的朋友会问我&am…

2026/9/7 0:47:43

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/7 0:14:19

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/7 0:14:17

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/7 0:03:36

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目:基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念,但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测,PyQt5 负责提供可视化的桌面交互界…

2026/9/7 0:03:36

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

简介:UL 1642是锂电池安全领域的重要规范,本中文版资源适合锂电池制造商、检测机构工程师及产品认证相关人员阅读,用于理解电池在设计与制造层面的安全要求、测试方法与合规要点。资源共1个PDF文件,压缩包大小834KB,便…

2026/9/7 0:03:36

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

简介:BS EN 13814-1:2019是英国采纳欧洲标准EN 13814-1:2019的正式版本,由BSI标准出版,重点规定游乐设施和游乐设备在设计与制造环节的安全准则,与BS EN 13814-2:2019、BS EN 13814-3:2019共同取代旧版BS EN 13814:2004。该标准面…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/6 19:33:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/6 10:19:40

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…