Skip to main content

预览

¥Preview

使用预览功能,你可以直接从 Strapi 的管理面板预览前端应用。这有助于了解内容管理器编辑视图中的内容更新将如何影响最终结果。

¥With the Preview feature, you can preview your front end application directly from Strapi's admin panel. This is helpful to see how updates to your content in the Edit View of the Content Manager will affect the final result.

IDENTITY CARD
Plan
Free feature
Live Preview available only with the CMS Growth and Enterprise plans.
Role & permission
Read permissions in Roles > Plugins - Users & Permissions
Activation
Should be configured in the config/admin file
Environment
Available in both Development & Production environment
Previewing contentPreviewing content

配置

¥Configuration

注意
  • 必须在你的 .env 文件中定义以下环境变量,用适当的值替换示例值:

    ¥The following environment variables must be defined in your .env file, replacing example values with appropriate values:

    CLIENT_URL=https://your-frontend-app.com
    PREVIEW_SECRET=your-secret-key

    PREVIEW_SECRET 键是可选的,但在 Next.js 草稿模式下是必需的。

    ¥The PREVIEW_SECRET key is optional but required with Next.js draft mode.

  • 应该已经创建并设置了你的 Strapi 项目的前端应用。

    ¥A front-end application for your Strapi project should be already created and set up.

配置组件

¥Configuration components

预览功能配置存储在 config/admin 文件preview 对象中,由 3 个关键组件组成:

¥The Preview feature configuration is stored in the preview object of the config/admin file and consists of 3 key components:

激活标志

¥Activation flag

启用或禁用预览功能:

¥Enables or disables the preview feature:

config/admin.ts|js
// …
preview: {
enabled: true,
// …
}
// …

允许的来源

¥Allowed origins

控制哪些域可以访问预览:

¥Controls which domains can access previews:

config/admin.ts|js
// …
preview: {
enabled: true,
config: {
allowedOrigins: env("CLIENT_URL"), // Usually your frontend application URL
// …
}
}
// …

预览处理程序

¥Preview handler

管理预览逻辑和 URL 生成,如以下基本示例所示,其中 uid 是内容类型标识符(例如,api::article.articleplugin::my-api.my-content-type):

¥Manages the preview logic and URL generation, as in the following basic example where uid is the content-type identifier (e.g., api::article.article or plugin::my-api.my-content-type):

config/admin.ts|js
// …
preview: {
enabled: true,
config: {
// …
async handler(uid, { documentId, locale, status }) {
const document = await strapi.documents(uid).findOne({ documentId });
const pathname = getPreviewPathname(uid, { locale, document });

return `${env('PREVIEW_URL')}${pathname}`
},
}
}
// …

以下基本实现指南中给出了 URL 生成逻辑 的示例。

¥An example of URL generation logic in given in the following basic implementation guide.

预览草稿条目

¥Previewing draft entries

前端应用查询草稿或已发布内容的策略是特定于框架的。至少存在 3 种策略:

¥The strategy for the front end application to query draft or published content is framework-specific. At least 3 strategies exist:

  • 使用查询参数,具有类似 /your-path?preview=true 的内容(例如,这是 Nuxt 的工作方式)

    ¥using a query parameter, having something like /your-path?preview=true (this is, for instance, how Nuxt works)

  • 重定向到专用预览路由,如 /preview?path=your-path(例如,这是 Next 的草稿模式 的工作方式)

    ¥redirecting to a dedicated preview route like /preview?path=your-path(this is, for instance, how Next's draft mode works)

  • 或使用不同的域进行预览,如 preview.mysite.com/your-path

    ¥or using a different domain for previews like preview.mysite.com/your-path.

当为你的内容类型启用 起草并发布 时,你还可以直接利用 Strapi 的 status 参数来处理预览处理程序中的逻辑,使用以下通用方法:

¥When Draft & Publish is enabled for your content-type, you can also directly leverage Strapi's status parameter to handle the logic within the Preview handler, using the following generic approach:

async handler(uid, { documentId, locale, status }) {
const document = await strapi.documents(uid).findOne({ documentId });
const pathname = getPreviewPathname(uid, { locale, document });
if (status === 'published') {
// return the published version
}
// return the draft version
},

基本实现指南 中给出了使用 Next.js 草稿模式的更详细示例。

¥A more detailed example using the draft mode of Next.js is given in the basic implementation guide.

基本实现指南

¥Basic implementation guide

按照以下步骤将预览功能添加到你的内容类型。

¥Follow these steps to add Preview capabilities to your content types.

1. [Strapi] 创建预览配置

¥ [Strapi] Create the Preview configuration

创建一个新的文件 /config/admin.ts(如果存在则更新它),具有以下基本结构:

¥Create a new file /config/admin.ts (or update it if it exists) with the following basic structure:

config/admin.ts
export default ({ env }) => ({
// Other admin-related configurations go here
// (see docs.strapi.io/cms/configurations/admin-panel)
preview: {
enabled: true,
config: {
allowedOrigins: env('CLIENT_URL'),
async handler (uid, { documentId, locale, status }) => {
// Handler implementation coming in step 3
},
},
},
});

2. [Strapi] 添加 URL 生成逻辑

¥ [Strapi] Add URL generation logic

使用 getPreviewPathname 函数添加 URL 生成逻辑。以下示例取自 启动板 Strapi 演示应用:

¥Add the URL generation logic with a getPreviewPathname function. The following example is taken from the Launchpad Strapi demo application:

config/admin.ts
// Function to generate preview pathname based on content type and document


const getPreviewPathname = (uid, { locale, document }): string => {


const { slug } = document;

// Handle different content types with their specific URL patterns
switch (uid) {
// Handle pages with predefined routes
case "api::page.page":
switch (slug) {
case "homepage":
return `/${locale}`; // Localized homepage
case "pricing":
return "/pricing"; // Pricing page
case "contact":
return "/contact"; // Contact page
case "faq":
return "/faq"; // FAQ page
}
// Handle product pages
case "api::product.product": {
if (!slug) {
return "/products"; // Products listing page
}
return `/products/${slug}`; // Individual product page
}
// Handle blog articles
case "api::article.article": {
if (!slug) {
return "/blog"; // Blog listing page
}
return `/blog/${slug}`; // Individual article page
}
default: {
return null;
}
}
};

// … main export (see step 3)
注意

如果没有意义,某些内容类型不需要预览,因此默认情况返回 null。例如,具有一些站点元数据的全局单一类型将没有匹配的前端页面。在这些情况下,处理程序函数应返回 null,并且预览 UI 将不会显示在管理面板中。这是你为每种内容类型启用或禁用预览的方式。

¥Some content types don't need to have a preview if it doesn't make sense, hence the default case returning null. A Global single type with some site metadata, for example, will not have a matching front-end page. In these cases, the handler function should return null, and the preview UI will not be shown in the admin panel. This is how you enable or disable preview per content type.

3. [Strapi] 添加处理程序逻辑

¥ [Strapi] Add handler logic

创建完整配置,扩展在步骤 1 中创建的基本配置。使用在步骤 2 中创建的 URL 生成逻辑,添加适当的处理程序逻辑:

¥Create the complete configuration, expanding the basic configuration created in step 1. with the URL generation logic created in step 2., adding an appropriate handler logic:

config/admin.ts


const getPreviewPathname = (uid, { locale, document }): string => {


// … as defined in step 2
};

// Main configuration export
export default ({ env }) => {
// Get environment variables
const clientUrl = env("CLIENT_URL"); // Frontend application URL
const previewSecret = env("PREVIEW_SECRET"); // Secret key for preview authentication

return {
// Other admin-related configurations go here
// (see docs.strapi.io/cms/configurations/admin-panel)
preview: {
enabled: true, // Enable preview functionality
config: {
allowedOrigins: clientUrl, // Restrict preview access to specific domain
async handler(uid, { documentId, locale, status }) {
// Fetch the complete document from Strapi
const document = await strapi.documents(uid).findOne({ documentId });

// Generate the preview pathname based on content type and document
const pathname = getPreviewPathname(uid, { locale, document });

// Disable preview if the pathname is not found
if (!pathname) {
return null;
}

// Use Next.js draft mode passing it a secret key and the content-type status
const urlSearchParams = new URLSearchParams({
url: pathname,
secret: previewSecret,
status,
});
return `${clientUrl}/api/preview?${urlSearchParams}`;
},
},
},
};
};

4. [前端] 设置前端预览路由

¥ [Front end] Set up the front-end preview route

设置前端预览路线高度依赖于前端应用使用的框架。

¥Setting up the front-end preview route is highly dependent on the framework used for your front-end application.

例如,Next.js 草稿模式Nuxt 预览模式 在各自的文档中提供了有关如何实现前端部分的附加文档。

¥For instance, Next.js draft mode and Nuxt preview mode provide additional documentation on how to implement the front-end part in their respective documentations.

如果使用 Next.js,基本实现可能类似于以下示例,该示例取自 启动板 Strapi 演示应用:

¥If using Next.js, a basic implementation could be like in the following example taken from the Launchpad Strapi demo application:

/next/api/preview/route.ts
import { draftMode } from "next/headers";
import { redirect } from "next/navigation";

export async function GET(request: Request) {
// Parse query string parameters
const { searchParams } = new URL(request.url);
const secret = searchParams.get("secret");
const url = searchParams.get("url");
const status = searchParams.get("status");

// Check the secret and next parameters
// This secret should only be known to this route handler and the CMS
if (secret !== process.env.PREVIEW_SECRET) {
return new Response("Invalid token", { status: 401 });
}

// Enable Draft Mode by setting the cookie
if (status === "published") {
draftMode().disable();
} else {
draftMode().enable();
}

// Redirect to the path from the fetched post
// We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities
redirect(url || "/");
}

5. [前端] 允许嵌入前端

¥ [Front end] Allow the front-end to be embedded

在 Strapi 方面,allowedOrigins 配置参数 允许管理面板在 iframe 中加载前端窗口。但允许嵌入是双向的,因此在前端,你还需要允许窗口嵌入到 Strapi 的管理面板中。

¥On the Strapi side, the allowedOrigins configuration parameter allows the admin panel to load the front-end window in an iframe. But allowing the embedding works both ways, so on the front-end side, you also need to allow the window to be embedded in Strapi's admin panel.

这要求前端应用具有自己的标头指令,即 CSP frame-ancestors 指令。设置此指令取决于你的网站是如何构建的。例如,在 Next.js 中设置此功能需要中间件配置(参见 Next.js 文档)。

¥This requires the front-end application to have its own header directive, the CSP frame-ancestors directive. Setting this directive up depends on how your website is built. For instance, setting this up in Next.js requires a middleware configuration (see Next.js docs).

6. [前端] 检测 Strapi 中的更改并刷新前端

¥ [Front end] Detect changes in Strapi and refresh the front-end

Strapi 发出 strapiUpdate 消息以通知前端数据已更改。

¥Strapi emits a strapiUpdate message to inform the front end that data has changed.

要跟踪这一点,请在前端应用中添加一个事件监听器来监听通过 postMessage() APIwindow 对象上发布的事件。监听器需要过滤消息并仅对 Strapi 发起的消息做出反应,然后刷新 iframe 内容。

¥To track this, within your front-end application, add an event listener to listen to events posted through the postMessage() API on the window object. The listener needs to filter through messages and react only to Strapi-initiated messages, then refresh the iframe content.

使用 Next.js,建议使用 `router.refresh()` 方法 来刷新 iframe 内容。

¥With Next.js, the recommended way to refresh the iframe content is with the `router.refresh()` method.

next/app/path/to/your/front/end/logic.jsx
export default function MyClientComponent({...props}) {
// …
const router = useRouter();

useEffect(() => {
const handleMessage = async (message) => {
if (
// Filters events emitted through the postMessage() API
message.origin === process.env.NEXT_PUBLIC_API_URL &&
message.data.type === "strapiUpdate"
) { // Recommended way to refresh with Next.js
router.refresh();
}
};

// Add the event listener
window.addEventListener("message", handleMessage);

// Cleanup the event listener on unmount
return () => {
window.removeEventListener("message", handleMessage);
};
}, [router]);

// ...
}
Caching in Next.js:

在 Next.js 中,缓存持久性 可能需要额外的步骤。你可能需要通过从客户端向服务器发出 API 调用来使缓存无效,重新验证逻辑将在服务器处理。有关详细信息,请参阅 Next.js 文档,例如 revalidatePath() 方法

¥In Next.js, cache persistence may require additional steps. You might need to invalidate the cache by making an API call from the client side to the server, where the revalidation logic will be handled. Please refer to Next.js documentation for details, for instance with the revalidatePath() method.

[前端] 后续步骤

¥[Front end] Next steps

设置预览系统后,你需要调整数据获取逻辑以适当处理草稿内容。这涉及以下步骤:

¥Once the preview system is set up, you need to adapt your data fetching logic to handle draft content appropriately. This involves the following steps:

  1. 创建或调整你的数据获取实用程序以检查是否启用了草稿模式

    ¥Create or adapt your data fetching utility to check if draft mode is enabled

  2. 更新 API 调用以在适当时包含草稿状态参数

    ¥Update your API calls to include the draft status parameter when appropriate

以下内容取自 启动板 Strapi 演示应用,是如何在 Next.js 前端应用中实现草稿感知数据获取的示例:

¥The following, taken from the Launchpad Strapi demo application, is an example of how to implement draft-aware data fetching in your Next.js front-end application:

import { draftMode } from "next/headers";
import qs from "qs";

export default async function fetchContentType(
contentType: string,
params: Record = {}
): Promise {
// Check if Next.js draft mode is enabled
const { isEnabled: isDraftMode } = draftMode();

try {
const queryParams = { ...params };
// Add status=draft parameter when draft mode is enabled
if (isDraftMode) {
queryParams.status = "draft";
}

const url = `${baseURL}/${contentType}?${qs.stringify(queryParams)}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`Failed to fetch data from Strapi (url=${url}, status=${response.status})`
);
}
return await response.json();
} catch (error) {
console.error("Error fetching content:", error);
throw error;
}
}

然后可以在页面组件中使用此实用方法根据预览状态获取草稿或已发布的内容:

¥This utility method can then be used in your page components to fetch either draft or published content based on the preview state:

// In your page component:


const pageData = await fetchContentType('api::page.page', {


// Your other query parameters
});

6. [前端] 检测 Strapi 中的更改并刷新前端

¥ [Front end] Detect changes in Strapi and refresh the front-end

Strapi 发出 strapiUpdate 消息以通知前端数据已更改。

¥Strapi emits a strapiUpdate message to inform the front end that data has changed.

要跟踪这一点,请在前端应用中添加一个事件监听器来监听通过 postMessage() APIwindow 对象上发布的事件。监听器需要过滤消息并仅对 Strapi 发起的消息做出反应,然后刷新 iframe 内容。

¥To track this, within your front-end application, add an event listener to listen to events posted through the postMessage() API on the window object. The listener needs to filter through messages and react only to Strapi-initiated messages, then refresh the iframe content.

使用 Next.js,建议使用 `router.refresh()` 方法 来刷新 iframe 内容。

¥With Next.js, the recommended way to refresh the iframe content is with the `router.refresh()` method.

next/app/path/to/your/front/end/logic.jsx
export default function MyClientComponent({...props}) {
// …
const router = useRouter();

useEffect(() => {
const handleMessage = async (message) => {
if (
// Filters events emitted through the postMessage() API
message.origin === process.env.NEXT_PUBLIC_API_URL &&
message.data.type === "strapiUpdate"
) { // Recommended way to refresh with Next.js
router.refresh();
}
};

// Add the event listener
window.addEventListener("message", handleMessage);

// Cleanup the event listener on unmount
return () => {
window.removeEventListener("message", handleMessage);
};
}, [router]);

// ...
}
Caching in Next.js:

在 Next.js 中,缓存持久性 可能需要额外的步骤。你可能需要通过从客户端向服务器发出 API 调用来使缓存无效,重新验证逻辑将在服务器处理。有关详细信息,请参阅 Next.js 文档,例如 revalidatePath() 方法

¥In Next.js, cache persistence may require additional steps. You might need to invalidate the cache by making an API call from the client side to the server, where the revalidation logic will be handled. Please refer to Next.js documentation for details, for instance with the revalidatePath() method.

[前端] 后续步骤

¥[Front end] Next steps

设置预览系统后,你需要调整数据获取逻辑以适当处理草稿内容。这涉及以下步骤:

¥Once the preview system is set up, you need to adapt your data fetching logic to handle draft content appropriately. This involves the following steps:

  1. 创建或调整你的数据获取实用程序以检查是否启用了草稿模式

    ¥Create or adapt your data fetching utility to check if draft mode is enabled

  2. 更新 API 调用以在适当时包含草稿状态参数

    ¥Update your API calls to include the draft status parameter when appropriate

以下内容取自 启动板 Strapi 演示应用,是如何在 Next.js 前端应用中实现草稿感知数据获取的示例:

¥The following, taken from the Launchpad Strapi demo application, is an example of how to implement draft-aware data fetching in your Next.js front-end application:

import { draftMode } from "next/headers";
import qs from "qs";

export default async function fetchContentType(
contentType: string,
params: Record = {}
): Promise {
// Check if Next.js draft mode is enabled
const { isEnabled: isDraftMode } = draftMode();

try {
const queryParams = { ...params };
// Add status=draft parameter when draft mode is enabled
if (isDraftMode) {
queryParams.status = "draft";
}

const url = `${baseURL}/${contentType}?${qs.stringify(queryParams)}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`Failed to fetch data from Strapi (url=${url}, status=${response.status})`
);
}
return await response.json();
} catch (error) {
console.error("Error fetching content:", error);
throw error;
}
}

然后可以在页面组件中使用此实用方法根据预览状态获取草稿或已发布的内容:

¥This utility method can then be used in your page components to fetch either draft or published content based on the preview state:

// In your page component:


const pageData = await fetchContentType('api::page.page', {


// Your other query parameters
});

用法

¥Usage

使用功能的路径: 内容管理器,内容类型的编辑视图

¥Path to use the feature: Content Manager, edit view of your content type

预览 vs. 实时预览

根据你的 CMS 计划,你使用预览的体验将有所不同:

¥Based on your CMS plan, your experience with Preview will be different:

正确设置预览功能后,内容管理器的编辑视图 右侧将显示一个打开预览按钮。单击它将显示你的内容预览,因为它将出现在你的前端应用中,但直接在 Strapi 的管理面板中。

¥Once the Preview feature is properly set up, an Open preview button is visible on the right side of the Content Manager's edit view. Clicking it will display the preview of your content as it will appear in your front-end application, but directly within Strapi's the admin panel.

Previewing contentPreviewing content

打开预览后,你可以:

¥Once the Preview is open, you can:

  • 点击左上角的关闭按钮 返回内容管理器的编辑视图,

    ¥click the close button in the upper left corner to go back to the Edit View of the Content Manager,

  • 在预览草稿和已发布版本之间切换(如果内容类型启用了 起草并发布),

    ¥switch between previewing the draft and the published version (if Draft & Publish is enabled for the content-type),

  • 然后单击右上角的链接图标 复制预览链接。根据你当前正在查看的预览选项卡,这将复制指向草稿预览或已发布版本的链接。

    ¥and click the link icon in the upper right corner to copy the preview link. Depending on the preview tab you are currently viewing, this will either copy the link to the preview of the draft or the published version.

此外,使用实时预览,你可以:

¥Additionally, with Live Preview, you can:

注意

在内容管理器的编辑视图中,如果有未保存的更改,则打开预览按钮将被禁用。保存最新更改,然后你就可以再次预览内容了。

¥In the Edit view of the Content Manager, the Open preview button will be disabled if you have unsaved changes. Save your latest changes and you should be able to preview content again.