
基于 Next.js 静态生成构建 Drupal 驱动博客cms-drupal 官方示例实战解析【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js本文以 Next.js 仓库中的 cms-drupal 示例 为主体完整讲解如何用 Next.js 的 Static Generation静态生成能力将 Drupal 作为数据源构建博客站点从示例脚手架的创建、环境变量配置到getStaticProps/getStaticPaths与 next-drupal 数据层的对接、动态路由的 fallback 机制再到草稿预览Draft Mode的实现原理。读完本文你可以完整复现并改造这套「Drupal Next.js」前后端分离的静态博客方案。一、示例定位与技术架构该示例官方定位是「A statically generated blog example using Next.js and Drupal」即展示 Next.js 的静态生成功能并以 Drupal 作为 CMS 数据源。从源码结构看整体技术栈如下数据源Drupal 站点安装 Next.js 模块即 next-drupal 服务端配套后暴露基于 JSON:API 风格的资源接口Next.js 端通过next-drupal包在构建期拉取内容渲染模式Pages Router getStaticProps/getStaticPaths构建期预渲染为静态 HTML属于典型的 SSG 场景样式方案Tailwind CSS见 tailwind.config.js、postcss.config.js正文富文本使用 CSS Modulespost-body.module.css。依赖清单非常精简核心依赖只有 5 个dependencies: { classnames: 2.3.1, date-fns: 2.28.0, next: latest, next-drupal: latest, react: 18.2.0, react-dom: 18.2.0 }其中next-drupal是与 Drupal 数据层交互的关键包next声明为latest意味着该示例始终跟随最新版 Next.js 运行。目录职责划分清晰目录/文件职责pages/index.js首页聚合文章列表pages/[...slug].js文章详情页catch-all 动态路由pages/api/preview.js开启草稿预览pages/api/exit-preview.js退出草稿预览lib/api.js媒体资源绝对 URL 拼接lib/constants.jsCMS 名称等站点常量components/header、hero-post、post-body 等 19 个 UI 组件二、快速开始脚手架与环境变量2.1 创建项目按 README 的方式使用create-next-app以 npm、Yarn 或 pnpm 三种方式之一引导出该示例npx create-next-app --example cms-drupal cms-drupal-appyarn create next-app --example cms-drupal cms-drupal-apppnpm create next-app --example cms-drupal cms-drupal-app创建完成后进入cms-drupal-app目录执行npm run dev脚本定义见 package.jsondev/build/start分别对应next、next build、next start即可启动本地开发。2.2 配置 Drupal 侧前端能跑起来的前提是 Drupal 站点已安装并配置好 Next.js 模块。README 明确指引在部署前需完成 Drupal 侧的 Next.js 模块配置并获得所需的环境变量官方 Get Started 指南发布在 next-drupal 官网属外部文档此处不重复其步骤。2.3 环境变量一览部署配置README 中的 Vercel 部署链接参数要求提供以下 7 个环境变量环境变量用途NEXT_PUBLIC_DRUPAL_BASE_URLDrupal 站点基础地址前端拼接媒体/资源绝对 URL 的基础见 lib/api.jsNEXT_IMAGE_DOMAIN允许next/image加载的 Drupal 资源域名DRUPAL_SITE_IDDrupal 站点标识next-drupal 多站点路由用DRUPAL_FRONT_PAGE指定 Drupal 前台页面标识DRUPAL_PREVIEW_SECRET草稿预览签名密钥保护/api/preview链路DRUPAL_NEXT_CLIENT_IDnext-drupal 客户端 OAuth IDDRUPAL_NEXT_CLIENT_SECRETnext-drupal 客户端 OAuth Secret源码中唯一直接读取的环境变量是NEXT_PUBLIC_DRUPAL_BASE_URL其用法极为直观// lib/api.js export function absoluteURL(url) { return ${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}${url}; }由于 JSON:API 返回的uri.url是相对路径如/sites/default/files/...所有封面图、头像在渲染前都要经过absoluteURL拼成完整地址。带NEXT_PUBLIC_前缀说明该值会被内联进浏览器 bundle其余带DRUPAL_前缀的变量仅在服务端构建/运行时被 next-drupal 与 API 路由消费。三、首页列表数据的静态生成pages/index.js 展示了 next-drupal 数据层最核心的用法——getResourceCollectionFromContextexport async function getStaticProps(context) { const posts await getResourceCollectionFromContext( node--article, context, { params: { include: field_image,uid,uid.user_picture, sort: -created, }, }, ); return { props: { posts }, }; }几个值得注意的实现细节资源类型node--article对应 Drupal 中 article 内容类型的 JSON:API 集合名博客文章全部建模为 Drupal 的 nodeinclude参数一次性预取封面图字段field_image、作者uid及其头像uid.user_picture避免前端逐条请求关联资源这是 JSON:API 的标准 include 机制sort: -created按创建时间倒序保证最新文章排在最前页面结构组件层将第一条作为heroPost大图主视觉见 hero-post.js其余进入MoreStories网格列表。渲染时从 Drupal 字段中取值的映射关系为标题post.title、封面post.field_image.uri.url、日期post.created、作者名post.uid.field_name、作者头像post.uid.user_picture.uri.url、摘要post.body.summary、路由别名post.path.alias。这里能看出 Drupal 字段命名field_前缀、uid、path.alias被完整映射进了 React 组件 propsnext-drupal 在中间完成了「JSON:API 原始响应 → 展平嵌套关联」的转换。四、文章详情catch-all 路由与 fallback 机制pages/[...slug].js 是整个示例最精巧的部分用一条 catch-all 路由承接所有文章详情页并配合 Drupal 的 URL alias 实现友好链接。4.1 构建期预计算所有路径export async function getStaticPaths(context) { return { paths: await getPathsFromContext(node--article, context), fallback: true, }; }getPathsFromContext让 next-drupal 自动遍历 Drupal 端全部 article 资源解析其path.alias生成路径列表开发者无需手写 slug 遍历逻辑。fallback: true意味着构建期只预渲染已知的文章路径运行时请求未预渲染的 URL例如刚在 Drupal 后台发布、尚未重新构建的文章时先展示 fallback 骨架页源码中渲染PostTitleLoading…/PostTitle同时在服务端按需执行getStaticProps完成渲染——这是 SSG 站点「内容实时可见」的关键策略。4.2 单篇文章 相关推荐export async function getStaticProps(context) { const post await getResourceFromContext(node--article, context, { params: { include: field_image,uid,uid.user_picture, }, }); let morePosts []; if (post) { morePosts await getResourceCollectionFromContext( node--article, context, { params: { include: field_image,uid,uid.user_picture, sort: -created, filter[id][condition][path]: id, filter[id][condition][operator]: , filter[id][condition][value]: post.id, }, }, ); } return { props: { preview: context.preview || false, post, morePosts, }, }; }「推荐更多文章」的查询用 JSON:API 标准 filter 表达filter[id][condition]三元组path / operator / value即id 当前文章id等价于 SQL 的WHERE id ! ?。此外context.preview || false把草稿模式状态透传给页面组件供头部展示「预览中」的提示条components/alert.js。组件侧还处理了两个健壮性细节router.isFallback为真时渲染加载占位而非真实内容!router.isFallback !post?.id时直接渲染 Next.js 的 404 错误页防止无效路径白屏。4.3 正文富文本渲染Drupal 返回的正文是body.processed服务端渲染好的 HTMLcomponents/post-body.js 用dangerouslySetInnerHTML注入外层包 Tailwind 容器控制排版宽度内层用 CSS Modules.content统一富文本中的标题、段落、图片样式export default function PostBody({ content }) { return ( div classNamemax-w-2xl mx-auto div className{styles.content} dangerouslySetInnerHTML{{ __html: content }} / /div ); }因为 HTML 由可信的 Drupal 服务端生成此处的dangerouslySetInnerHTML是 SSG 场景的常见做法。五、草稿预览Draft Mode链路示例实现了 Drupal 后台编辑 → 前端实时预览的完整闭环由两个 API 路由配合开启预览pages/api/preview.js 仅一行——import { DrupalPreview } from next-drupal; export default DrupalPreview();next-drupal 提供的DrupalPreview()工厂返回一个 API handlerDrupal 编辑页的「Preview」按钮携带签名跳转到该路由路由校验依赖DRUPAL_PREVIEW_SECRET与 client 凭据后调用 Next.js 的 Draft Mode将preview: true写入请求上下文后续的getStaticProps即可拿到草稿态内容context.preview退出预览pages/api/exit-preview.js 展示了标准写法export default async function exit(_, res) { // Exit Draft Mode by removing the cookie res.setDraftMode({ enable: false }); // Redirect the user back to the index page. res.writeHead(307, { Location: / }); res.end(); }setDraftMode({ enable: false })清除 Draft Mode cookie再以 307 临时重定向回首页使浏览器缓存的草稿数据失效、重新走静态资源。这条链路的价值在于Drupal 后台的内容变更不必等待next build重新构建编辑者即可逐篇核对排版效果是 decoupled CMS 工作流中体验闭环的最后一环。六、构建与部署本地验证流程即标准 Next.js 三步npm run dev开发、npm run build构建构建期会真正向 Drupal 请求数据并预渲染全部getStaticPaths返回的路径、npm run start启动生产服务器。README 提供了两条部署路径一是通过 Vercel 的模板一键部署模板会自动提示填写上文 7 个环境变量二是自行配置 Drupal 模块后部署自有版本。需要注意两点其一Vercel 部署链接中的envDescription文案写着「connect the app with ghost」这是该示例 README 从 cms-ghost 复制时遗留的文案瑕疵实际填写的仍是 Drupal 变量其二示例声明的next为latest版本行为以当前 Next.js 版本的 Static Generation 语义为准。七、在 CMS 示例矩阵中的位置与延伸cms-drupal 是 Next.js 仓库 CMS 示例家族中的一员兄弟示例可参考cms-ghost、cms-datocms、cms-contentful、cms-wordpress、cms-sanity 等。对比阅读会发现各示例共享同一套「静态博客骨架」hero 主视觉 更多文章网格 正文页 相关推荐差异只在数据获取层Drupal 版本用getResourceCollectionFromContext/getResourceFromContext/getPathsFromContext三个函数封装了 JSON:API 访问并内置了 Draft Mode 支持——这也是它与多数静态 CMS 示例相比最突出的工程化亮点。若要继续改造该示例建议的切入点依次是在getStaticProps的params中增加 Drupal 过滤条件如按栏目筛选、利用revalidate配置增量静态再生成以缩短发布延迟、或参考 lib/constants.js 扩展站点级配置当前已定义CMS_NAME、EXAMPLE_PATH、HOME_OG_IMAGE_URL等常量供全局引用。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考