服务端实时预览

服务器端 Live Preview 仅适用于支持 Server Components 概念的前端框架,例如 React Server Components。如果你的前端应用是使用客户端框架构建的,如 Next.js Pages RouterReact RouterVue 3 等,请参阅 客户端 Live Preview

服务器端 Live Preview 的工作原理是每次保存文档(草稿保存、自动保存或发布)时都会向服务器发起一次往返请求。在使用 Live Preview 时,Admin Panel 会发出一个新的 window.postMessage 事件,你的前端应用可以利用这个事件来触发此过程。在 Next.js 中,这意味着只需调用 router.refresh(),即可使用来自 Local API 的新数据重新水合 HTML。

建议你在启用 Live Preview 的同时也启用 Autosave,以获得更流畅的体验。

如果你的前端应用是使用 React 构建的,可以使用 Payload 提供的 RefreshRouteOnChange 函数。未来,Vue 和 Svelte 等其他主流框架也将获得官方支持。如果你现在正在使用这些框架,仍然可以利用 Payload 提供的底层工具自行集成 Live Preview。更多信息请参阅 构建你自己的路由刷新组件

React

如果你的前端应用使用服务器端 React 构建,比如 Next.js App Router,你可以使用 Payload 提供的 RefreshRouteOnSave 组件。

首先,安装 @payloadcms/live-preview-react 包:

npm install @payloadcms/live-preview-react

然后,在你的 page.tsx 文件中任意位置渲染 RefreshRouteOnSave 组件。以下是一个示例:

page.tsx:

import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayload } from 'payload'
import config from '../payload.config'

export default async function Page() {
  const payload = await getPayload({ config })

  const page = await payload.findByID({
    collection: 'pages',
    id: '123',
    draft: true,
  })

  return (
    <Fragment>
      <RefreshRouteOnSave />
      <h1>{page.title}</h1>
    </Fragment>
  )
}

RefreshRouteOnSave.tsx:

'use client'
import { RefreshRouteOnSave as PayloadLivePreview } from '@payloadcms/live-preview-react'
import { useRouter } from 'next/navigation.js'
import React from 'react'

export const RefreshRouteOnSave: React.FC = () => {
  const router = useRouter()

  return (
    <PayloadLivePreview
      refresh={() => router.refresh()}
      serverURL={process.env.NEXT_PUBLIC_PAYLOAD_URL}
    />
  )
}

构建你自己的路由刷新组件

无论你使用什么前端框架,都可以利用 Payload 提供的底层工具来构建自己的组件。

首先,安装基础包 @payloadcms/live-preview

npm install @payloadcms/live-preview

这个包提供了以下函数:

PathDescription
ready发送 window.postMessage 事件到 Admin Panel,表示前端已准备好接收消息。
isDocumentEvent检查 MessageEvent 是否来自 Admin Panel 并且是文档级别的事件(如草稿保存、自动保存、发布等)。

通过这些函数,你可以使用你选择的前端框架构建自己的钩子:

import { ready, isDocumentEvent } from '@payloadcms/live-preview'

// 构建你自己的组件步骤:
// 1. 监听来自 Admin Panel 的文档级别 `window.postMessage` 事件
// 2. 告诉 Admin Panel 何时准备好接收消息
// 3. 每次收到新的文档级别事件时刷新路由
// 4. 在组件卸载时取消订阅 `window.postMessage` 事件

以下是上面提到的 RefreshRouteOnSave React 组件的底层实现示例:

'use client'

import type React from 'react'

import { isDocumentEvent, ready } from '@payloadcms/live-preview'
import { useCallback, useEffect, useRef } from 'react'

export const RefreshRouteOnSave: React.FC<{
  apiRoute?: string
  depth?: number
  refresh: () => void
  serverURL: string
}> = (props) => {
  const { apiRoute, depth, refresh, serverURL } = props
  const hasSentReadyMessage = useRef<boolean>(false)

  const onMessage = useCallback(
    (event: MessageEvent) => {
      if (isDocumentEvent(event, serverURL)) {
        if (typeof refresh === 'function') {
          refresh()
        }
      }
    },
    [refresh, serverURL],
  )

  useEffect(() => {
    if (typeof window !== 'undefined') {
      window.addEventListener('message', onMessage)
    }

    if (!hasSentReadyMessage.current) {
      hasSentReadyMessage.current = true

      ready({
        serverURL,
      })
    }

    return () => {
      if (typeof window !== 'undefined') {
        window.removeEventListener('message', onMessage)
      }
    }
  }, [serverURL, onMessage, depth, apiRoute])

  return null
}

示例

要查看实际运行示例,请参考官方的 Live Preview 示例。该示例完整展示了如何在 Next.js App Router 应用中实现 Live Preview 功能。

故障排除

更新不如客户端 Live Preview 响应快

如果你发现更新不如客户端 Live Preview(即 useLivePreview 钩子)响应迅速,这是因为两者的工作机制不同——服务器端 Live Preview 是在文档被 保存 后刷新路由,而不是针对 表单状态 触发事件。

可以使用 Autosave 在服务器端模拟这种效果。尝试减小 versions.autoSave.interval 的值,使体验更加灵敏:

// collection.ts
{
   versions: {
    drafts: {
      autosave: {
        interval: 375,
      },
    },
  },
}

Iframe 拒绝连接

如果你的前端应用设置了 内容安全策略 (CSP) 阻止管理面板加载前端应用,iframe 将无法加载你的站点。要解决此问题,可以在 CSP 中通过设置 frame-ancestors 指令将管理面板的域名加入白名单:

frame-ancestors: "self" localhost:* https://your-site.com;