资讯动态

Next.js × Enterspeed:构建静态生成博客的完整实战指南(SSG + Preview Mode)

发布时间:2026/9/7 3:00:48 来源:尧图企业网站定制
Next.js × Enterspeed构建静态生成博客的完整实战指南SSG Preview Mode【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js本篇指南基于 Next.js 官方仓库中的cms-enterspeed示例examples/cms-enterspeed/README.md系统讲解如何使用 Next.js 的静态生成Static Generation能力结合 Enterspeed 这一 CMS 平台完成内容摄取Ingest、Schema 视图定义、数据拉取与博客渲染的全流程。读完本文你将掌握如何向 Enterspeed 的 Ingest API 提交blog/blogPost两类内容、如何编写Blog list与Blog post两个 Schema、如何用getStaticProps/getStaticPaths拉取 Delivery API 数据以及如何通过 Next.js 的 Draft Mode 实现发布前预览。示例定位与技术栈该示例是一个静态生成的博客Enterspeed 作为数据源。核心依赖非常精简见 package.json依赖版本用途nextlatest框架主体提供 SSG 与 Draft Modereact/react-dom^18.2.0UI 渲染date-fns2.28.0文章日期格式化classnames2.3.1组件类名组合tailwindcssdev^3.0.15样式配置见 tailwind.config.js仓库中还提供线上演示站点next-blog-demo.enterspeed.com供参考效果。目录结构一览均位于examples/cms-enterspeed/下pages/index.tsx博客首页通过getStaticProps拉取文章列表pages/posts/[slug].tsx文章详情页getStaticPathsgetStaticPropspages/api/preview.js 与 pages/api/exit-preview.jsPreview Mode 的进入/退出入口lib/api.ts封装 Enterspeed Delivery API 的调用types/postType.ts 与 types/authorType.ts内容 TypeScript 类型components/hero-post、post-body、post-preview、meta等展示组件。快速开始用 create-next-app 引导项目按 README 说明使用create-next-app引导示例npx create-next-app --example cms-enterspeed enterspeed-appyarn create next-app --example cms-enterspeed enterspeed-apppnpm create next-app -- --example cms-enterspeed enterspeed-app随后进入enterspeed-app目录按下面的配置步骤操作即可。第 1 步配置 Enterspeed 账号在开始之前需要一个 Enterspeed 站点到 Enterspeed 官网注册即可。创建账号后你需要依次完成创建一个tenant租户创建一个data source数据源创建一个environment环境创建并配置domains域名创建一个environment client环境客户端。其中第 5 步生成的 API Key 就是我们后续写入ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY环境变量的值。这个 Key 会作为X-Api-Key请求头随每次 Delivery API 调用发出——这一点可以直接在 lib/api.ts 中印证const call async (query: string, preview: boolean) { const url https://delivery.enterspeed.com/v1?${query}; const response await fetch(new Request(url), { headers: { Content-Type: application/json, X-Api-Key: preview ? PREVIEW_API_KEY : PRODUCTION_API_KEY, }, }); return response.json(); };可以看到正式数据与预览数据分别使用ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY和ENTERSPEED_PREVIEW_ENVIRONMENT_API_KEY两个 Key通过preview布尔参数切换这正是 Preview Mode 双数据源机制的落点。第 2 步向 Enterspeed 摄取内容Ingest API内容需要从现有 CMS / PIM 等来源进入 Enterspeed可以使用 Enterspeed 提供的集成或者直接用 Ingest API。示例采用 curl 请求把演示数据灌入 Enterspeed。需要摄取两种内容类型blog类型作为博客文章的集合 / 父节点blogPost类型具体的博客文章实体通过originParentId挂到blog节点之下。先创建blog类型注意/ingest/v2/1中的1即该实体在 Enterspeed 中的 IDcurl --location --request POST https://api.enterspeed.com/ingest/v2/1 \ --header X-Api-Key: [YOUR DATA SOURCE API KEY] \ --header Content-Type: application/json \ --data-raw { type: blog, url: /blog }再摄取一篇具体的博客文章originParentId: 1指向上面创建的blog实体curl --location --request POST https://api.enterspeed.com/ingest/v2/2 \ --header X-Api-Key: [YOUR DATA SOURCE API KEY] \ --header Content-Type: application/json \ --data-raw { type: blogPost, url: /preview-mode-for-static-generation, originParentId: 1, properties: { title: Preview Mode for Static Generation, featuredImage: https://res.cloudinary.com/enterspeed/image/upload/v1648804237/Next.js%20-%20Example%20With%20Enterspeed/cover5.webp, date: 2022-04-01T01:07:42, author: { name: Vercel Team, avatar: { url: https://res.cloudinary.com/enterspeed/image/upload/v1648804719/Next.js%20-%20Example%20With%20Enterspeed/vercel-avatar.webp } }, categories: [Next.js, Static Generation], excerpt: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilisi morbi tempus., content: pLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilisi morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies./ppVenenatis cras sed felis eget velit. Consectetur libero id faucibus nisl tincidunt. Gravida in fermentum et sollicitudin ac orci phasellus egestas tellus. Volutpat consequat mauris nunc congue nisi vitae. Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Nibh cras pulvinar mattis nunc sed. Luctus accumsan tortor posuere ac ut consequat semper viverra. Fringilla ut morbi tincidunt augue interdum velit euismod./ph2Lorem Ipsum/h2pTristique senectus et netus et malesuada fames ac turpis. Ridiculus mus mauris vitae ultricies leo integer malesuada nunc vel. In mollis nunc sed id semper. Egestas tellus rutrum tellus pellentesque. Phasellus vestibulum lorem sed risus ultricies tristique nulla. Quis blandit turpis cursus in hac habitasse platea dictumst quisque. Eros donec ac odio tempor orci dapibus ultrices. Aliquam sem et tortor consequat id porta nibh. Adipiscing elit duis tristique sollicitudin nibh sit amet commodo nulla. Diam vulputate ut pharetra sit amet. Ut tellus elementum sagittis vitae et leo. Arcu non odio euismod lacinia at quis risus sed vulputate./p, tags: [SSG, Preview] } }请求体中的properties字段即最终前端渲染所需的全部字段title、featuredImage、date、author含头像、categories、excerpt、contentHTML 字符串、tags。这些字段与前端类型定义 types/postType.ts 完全对应type PostType { url: string; title: string; featuredImage: string; date: string; author: AuthorType; excerpt: string; categories: string[]; content: string; tags: string[]; };第 3 步原文档 Step 4在 Enterspeed 中创建 Schema内容摄取完成后接下来是创建 Schema。Enterspeed 的 Schema 用于把原始数据转换成生成的视图views供前端应用直接消费——这是该示例区别于普通 REST API 型 CMS 的关键前端不直接消费原始实体而是消费 Schema 生成的、结构化的视图。需要创建两个 SchemaBlog list和Blog post。将下面的代码填入对应 Schema保存并部署deploy后Enterspeed 会基于已摄取的内容生成视图。Blog listSchema这个 Schema 的要点sourceEntityTypes指定blog类型实体作为源route.handles声明路由句柄blogList前端正是用它拉取blogListItems通过$lookup查找所有originParentId等于当前blog实体originId的文章并映射出列表所需字段{ sourceEntityTypes: [blog], route: { handles: [blogList] }, properties: { blogListItems: { type: array, input: { $lookup: { operator: equals, sourceEntityProperty: originParentId, matchValue: {originId} } }, items: { type: object, properties: { url: {item.url}, title: {item.properties.title}, featuredImage: {item.properties.featuredImage}, date: {item.properties.date}, excerpt: {item.properties.excerpt}, author: { type: object, properties: { name: {item.properties.author.name}, avatar: { type: object, properties: { url: {item.properties.author.avatar.url} } } } } } } } } }Blog postSchema文章详情 Schema 的要点以blogPost实体为源route.url直接取自实体的{url}因此文章 URL 为/preview-mode-for-static-generation构建出的页面即/posts/preview-mode-for-static-generationactions中声明了process动作并关联originParentId{p.xxx}是属性字段的简写引用{ sourceEntityTypes: [blogPost], route: { url: {url} }, actions: [ { type: process, originId: { $exp: {originParentId} } } ], properties: { url: {url}, type: {type}, title: {p.title}, featuredImage: {p.featuredImage}, date: {p.date}, author: { type: object, properties: { name: {p.author.name}, avatar: { type: object, properties: { url: {p.author.avatar.url} } } } }, categories: { type: array, input: {p.categories}, items: { type: string, value: {item} } }, tags: { type: array, input: {p.tags}, items: { type: string, value: {item} } }, content: {p.content} } }第 4 步原文档 Step 5配置环境变量复制示例目录下的.env.local.example为.env.local该文件被 Git 忽略cp .env.local.example .env.local仓库中 .env.local.example 的内容为ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY # Only required if you want to enable preview mode # ENTERSPEED_PREVIEW_ENVIRONMENT_API_KEY # ENTERSPEED_PREVIEW_SECRET随后在.env.local中把ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY设置为在 Enterspeed 中创建的 Environment client API key。三个变量的职责如下变量必需性作用ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY必需生产环境客户端 KeySSG 构建/请求时拉取已发布内容ENTERSPEED_PREVIEW_ENVIRONMENT_API_KEY仅预览模式需要预览数据源的环境客户端 KeyENTERSPEED_PREVIEW_SECRET仅预览模式需要进入 Preview Mode 的口令任意随机字符串即可建议 URL 友好第 5 步原文档 Step 6开发模式运行npm install npm run dev # 或 yarn install yarn dev对应 package.json 中的脚本dev为nextbuild为next buildstart为next start。启动后博客运行在http://localhost:3000。前端如何消费 Enterspeed 视图SSG 实现细节首页 pages/index.tsx 的getStaticProps通过句柄blogList拉取列表视图export async function getStaticProps({ preview }: { preview: boolean }) { const data await getByHandle(blogList, preview); return { props: { posts: data.blogListItems, preview: preview || null, }, }; }其中getByHandle的实现lib/api.ts会请求https://delivery.enterspeed.com/v1?handleblogList并返回响应体中views[handle]即前面Blog listSchema 声明的route.handles与 Schemaproperties的对应关系在这里闭环。文章详情页 pages/posts/[slug].tsx 则展示了完整的getStaticPathsgetStaticProps组合export async function getStaticPaths({ preview }: { preview: boolean }) { const data await getByHandle(blogList, preview); return { paths: data.blogListItems.map((post) ({ // Remove starting and ending slash from url params: { slug: post.url.replace(/^\/|\/$/g, ) }, })), fallback: false, }; } export async function getStaticProps({ params, preview, }: { params: Params; preview: boolean; }) { // Adding starting slash to the URL again const data await getByUrl(encodeURIComponent(/${params.slug}), preview); return { props: { post: data, preview: preview || null, }, }; }几个值得注意的实现细节slug 与 URL 的换算getStaticPaths中把 Enterspeed 返回的post.url如/preview-mode-for-static-generation去掉首尾斜杠作为动态段sluggetStaticProps中再补回前导斜杠并做encodeURIComponent交给getByUrl请求?url...查询。getByUrl返回的是响应体中的route字段即Blog postSchema 按route.url匹配到的视图fallback: false未在构建时预渲染的 URL 直接 404组件内还有兜底逻辑——router.isFallback时显示Loading…!post?.url时渲染ErrorPage statusCode{404}preview透传两个数据获取函数都接收 Next.js 注入的previewDraft Mode 激活状态据此切换生产/预览 API Key并把preview: preview || null传给组件控制 UI如页头提示。页面样式方面next.config.js 声明了next/image的远程图源白名单演示图托管在 Cloudinarymodule.exports { images: { remotePatterns: [ { protocol: https, hostname: res.cloudinary.com, port: , pathname: /my-account/**, }, ], }, };如果演示封面图换到其他 CDN需要相应调整hostname与pathname否则next/image会拒绝加载。第 6 步原文档 Step 7可选配置 Preview Mode此步骤可选。大多数 CMS 都支持发布前预览。Enterspeed 的做法是为预览数据单独建一个 data source让 CMS 把草稿内容同时发送到这个预览数据源官方例如在 Enterspeed Umbraco 集成中实现了该机制。完成后基于预览数据源创建一个新的 environment client把新客户端的 API Key 填入.env.local的ENTERSPEED_PREVIEW_ENVIRONMENT_API_KEY把ENTERSPEED_PREVIEW_SECRET设为任意随机字符串建议 URL 友好。此时.env.local应为ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY # Only required if you want to enable preview mode ENTERSPEED_PREVIEW_ENVIRONMENT_API_KEY ENTERSPEED_PREVIEW_SECRET重要修改环境变量后需要重启 Next.js 服务器使其生效。预览模式的源码机制进入与退出 Preview Mode 分别由两个 API Route 完成。pages/api/preview.jsif (req.query.secret ! process.env.ENTERSPEED_PREVIEW_SECRET) { return res.status(401).json({ message: Invalid token }); } res.setDraftMode({ enable: true }); res.redirect(/);它校验 query 中的secret与环境变量一致后调用res.setDraftMode({ enable: true })写入 Draft Mode cookie再重定向回首页。此后构建期getStaticProps/getStaticPaths中的preview参数变为truelib/api.ts自动改用预览 API Key 拉取草稿数据。退出则由 pages/api/exit-preview.js 完成res.setDraftMode({ enable: false })移除 cookie并以307重定向回首页。第 7 步原文档 Step 8体验 Preview Mode直接访问http://localhost:3000时你看到的仍是已发布内容。要看到预览草稿数据访问http://localhost:3000/api/preview?secretsecret其中secret即你在ENTERSPEED_PREVIEW_SECRET中设置的字符串。激活后即可在博客中看到草稿文章如演示的 Preview Mode 文章。退出预览模式访问http://localhost:3000/api/exit-preview部署可以将该应用部署到 Vercel。两种方式部署本地项目把项目推送到 Git 仓库后在 Vercel 导入。注意导入时务必在 Environment Variables 中配置与.env.local一致的三个变量使用模板一键部署通过 Vercel 的 Deploy with Vercel 按钮克隆本示例部署系统会提示填写ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY连接 Enterspeed 所必需。相关示例仓库examples/目录下还有一系列同架构SSG 博客的 CMS 集成示例可对照参考例如AgilityCMSBuilder.ioButterCMSContentfulCosmicDatoCMSDotCMSDrupalGhostGraphCMSKontent.aiPayloadPrismicSanityStoryblokUmbracoWordPressBlog Starter小结cms-enterspeed示例完整演示了内容摄取 → Schema 视图 → SSG 渲染 → Draft Mode 预览这条静态生成博客的闭环链路Enterspeed 侧通过 Ingest API 与两个 SchemaBlog list用句柄路由、Blog post用 URL 路由生成结构化视图Next.js 侧通过 lib/api.ts 封装的两个函数getByHandle/getByUrl配合getStaticProps/getStaticPaths完成构建期渲染并用双 API Key setDraftMode实现发布前预览。理解这套模式后你可以把它迁移到任意提供视图/查询接口的 CMS 上只需替换lib/api.ts中的数据获取实现。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价