introduction

预览模式,在例如博客文章等相关地方,或者预设主题时使用的应该非常多 …

我们之前学习了如何使用预渲染(例如静态生成), 但是对于我们使用预览模式来说,这不方便,,因为有些时候我们需要页面立即将修改的东西进行预览,并且这些页面是在请求时渲染而不是构建时(并且是抓取草案内容而不是发布的内部),所以我们就需要使用预览模式解决这个问题 ..

第一步,创建并访问预览API 路由

假设,预览API 路由名称为: pages/api/preview.js或者.ts

在API 路由中,我们需要设置setPreviewData在响应体对象上,这个setPreviewData的参数应该是一个对象,并且它应该能够被getStaticProps所使用,现在它是一个{}

res.setPreviewData设置某些Cookie在浏览器上(在预览模式中会打开),任何包含这些cookies的发送给Next.js的请求都会考虑为预览模式,并且这个行为对于静态生成的页面将会发生改变 ..

你能够测试它(创建一个API 路由并访问它) ..

  1. // A simple example for testing it manually from your browser.
  2. // If this is located at pages/api/preview.js, then
  3. // open /api/preview from your browser.
  4. export default function handler(req, res) {
  5. res.setPreviewData({})
  6. res.end('Preview mode enabled')
  7. }

如果使用的浏览器开发者工具,你可以注意到__prerender_bypass以及 __next_preview_datacookie 将被在这个请求上设置 ..

从无头CMS上安全的访问它

事实上,你想要从无头CMS上安全的调用API 路由,这特定的步骤将会依赖于你使用的CMS,但是这里有一些常见步骤需要你注意:

这些步骤假设无头CMS(你能够支持配置自定义的预览URL),如果不可能,你仍然能够使用这个安全访问预览URL …

但是你需要手动的构造并访问这个预览URL …

  • 首先,你需要创建一个加密token 字符串(通过你选择的token 生成器),这个密钥仅仅能够被你的next.js app 和CMS 知道,这个密钥阻止了不能从预览URL 上访问MCS …
  • 第二,如果你的CMS 支持设置自定义的预览URL,指定如下作为预览URL(假设你的预览API 路由位于·pages/api/preview.js)
  1. https://<your-site>/api/preview?secret=<token>&slug=<path>
  • 站点 部署的域名
  • token 生成的安全加密token
  • 路径 想要进行预览的页面路径,如果你想要预览/posts/foo,然后你应该使用&slug=/posts/foo

你的无头CMS可能允许你包括一个变量在预览URL 中,因此path能够基于CMS的数据动态设置 …,例如slug=/posts/{entry.fields.slug}

最终,在预览API 路由中:

  • 检查secret是否匹配并且slug参数存在,如果不存在请求失败)
  • 调用res.setPreviewData
  • 然后重定向浏览器到由slug指定的路径 (例如这个示例使用307 重定向) ..)

如果成功,浏览器将重定向到你想预览的路径(使用已经被设置的预览模式 cookies) ..

第二步,更新getStaticProps

这一步更新getStaticProps来支持预览模式 ..

如果你请求一个页面(它有一个使用了预览模式Cookies设置的getStaticProps),那么getStaticProps将会在请求时进行调用(而不是构建时) …

因此,它将被调用且有一个context对象能够被使用

  • context.preview= true
  • context;.previewData将等价于setPreviewData设置的参数 …

于是当我们使用res.setPreviewData({})在预览API 路由中,那么context.previewData将会是{},你能够使用它从预览API 路由传递会话信息到 getStaticProps(如果有必要) ..

如果你还使用getSaticPaths,那么context.params将也是可用的 ..

抓取预览数据

现在我们可以基于context对象来抓取不同的数据 更新getStaticProps,举个例子,如果无头CMS 可能包含了对草稿文章的不同API 端点,你能够使用context.preview修改API 端点URL …

  1. export async function getStaticProps(context) {
  2. // If context.preview is true, append "/preview" to the API endpoint
  3. // to request draft data instead of published data. This will vary
  4. // based on which headless CMS you're using.
  5. const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  6. // ...
  7. }

这就是全部了,如果你访问预览API 路由(使用secret以及 slug)从无头CMS或者手动访问,你应该现在能够查看预览内容了,并且如果你更新你的草案而不是发布,你应该能够预览草案 …

这里的意思就是从CMS 发送请求到API 预览路由(或者手动发送请求) 应该能够看到预览内容 ….

更多细节

在渲染next/router时暴露了一个isPreview标志,查看路由对象文档了解更多 …

清理预览模式cookies

默认对于预览模式的cookie没有过期时间,因此在浏览器关闭时预览会话将会结束 ..

为了手动清理预览模式cookie,创建一个API 路由然后调用即可 ..

  1. // pages/api/clear-preview-mode-cookies.js
  2. export default function handler(req, res) {
  3. res.clearPreviewData()
  4. }

然后,发送一个请求到/api/clear-preview-mode-cookies执行API 路由即可清理..

如果通过next/link调用这个路由,你必须传递prefetch={false}去阻止在链接预抓取之前调用clearPreviewData….

指定预览模式的周期

setPreviewData可以携带第二个可选的参数,它们应该是一个可选对象,接收如下的属性

  • max-Age 预览会话的最大时间(秒)
  • path指定cookie应该应用在那个路径之下,默认是/,对全部路径启用预览模式 …
  1. setPreviewData(data, {
  2. maxAge: 60 * 60, // The preview mode cookies expire in 1 hour
  3. path: '/about', // The preview mode cookies apply to paths with /about
  4. })

previewData尺寸限制

你能够传递一个对象到 setPreviewData并且它可以在getStaticProps中可用,然后由于这些数据是存储在cookie中,那么存在容量尺寸限制,目前预览数据的尺寸为2KB …

getServerSideProps协同工作

这个预览模式同样可以和它工作,它同样可以包含一个context 对象(内容一致) ..

与API 路由协同工作

API 路由能够在请求对象中访问preview以及 previewData ..

  1. export default function myApiRoute(req, res) {
  2. const isPreview = req.preview
  3. const previewData = req.previewData
  4. // ...
  5. }

每一次next build独一无二

绕过 Cookie 值和用于加密预览的私钥在next build完成时都会更改。这可确保无法猜到 Cookie 内容。

在基于Http 的本地浏览器上测试预览模式需要允许第三方cookies 以及本地存储访问 …

也就是next 对于cookie中存放的内容进行了私钥加密, 那么保障了一定的安全 …