前一篇設定了 Sanity Visual Editing 到頁面上,但是也因此影響了 Next.js 的前端頁面,讓整個前端頁面都套用了 Visual Editing 的 click-to-edit。
本篇要實作的就是如何整合 Next.js 的 Draft Mode 跟 Sanity Visual Editing ,讓只有在 Visual Editing 的頁面 Next.js 有 click-to-edit 的功能。
在開始之前要先了解的是 Next.js 的 Draft Mode。
Next.js 的 Draft Mode 主要是解決在跟 headless CMS 平台整合開發時,有時候有新內容必須要發佈時,必須要發佈才能在 Next.js 的專案內看到,或是有些按鈕、功能只有在 Draft Mode 中才要啟用。
在 Next.js 中要啟用 Draft Mode 的話要從 next/headers 引入 draftMode
方法,並且透過互叫 enable()
方法來啟用 Draft Mode。
建立一個 ./app/api/draft-mode/enable/route.ts
檔案作為啟用 Draft Mode 的 api:
// ./app/api/draft-mode/enable/route.ts
import { draftMode } from "next/headers";
export async function GET(request: Request) {
draftMode().enable();
return new Response("Draft mode is enabled");
}
只要這樣只要在瀏覽器呼叫 /api/draft-mode/enable
就會啟用 Next.js 的 Draft Mode 了。
其實 draftMode().enable()
背後做的事情就是呼叫了 Set-Cookie 寫入了一個 key
是 __prerender_bypass
的 cookie。
要關閉的話也很簡單,只要簡單的使用 draftMode().disable()
方法就可以了:
// ./app/api/draft-mode/disable/route.ts
import { draftMode } from "next/headers";
export async function GET(request: Request) {
draftMode().disable();
return new Response("Draft mode is disabled");
}
而 draftMode().disable()
背後做的事情也很單純,就是把 __prerender_bypass
再刪掉而已。
經過上面設定過後,就可以回到 Next.js 的設定以 isEnabled
來判斷是否在前端頁面中啟用 Visual Editing 了。
// ...
import { VisualEditing } from "next-sanity";
import { draftMode } from "next/headers";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const isEnabled = draftMode().isEnabled;
return (
<html lang="en">
<body>
{/* ... */}
{children}
{isEnabled && <VisualEditing />} {/* 只有在 draftMode 中才啟用 */}
</body>
</html>
);
}
現在有了 Draft Mode 跟非 Draft Mode 的畫面了,下一步就是要讓 Sanity Visual Editing 使用的是 Draft Mode,而一般用戶使用的則是非 Draft Mode 畫面,
Sanity 設定很簡單,只要在 Sanity 專案中更改 presentationTool
中 previewUrl 的設定就好了。從原本的了字串 http://localhost:3000
改為 previewMode 的物件:
// ...
import {presentationTool} from 'sanity/presentation'
export default defineConfig({
// ...
plugins: [
// ...
presentationTool({
previewUrl: {
previewMode: {
enable: 'http://localhost:3000/api/draft-mode/enable',
},
},
}),
],
// ...
})
這意味著 Sanity 用 Vistio Editing 頁面打開時,會先去 enable 頁面打開 Draft Mode 後再訪問該頁面,像是要訪問這頁:
就會在訪問這頁之前先去對 /api/draft-mode/enable
發出請求,啟用網頁的 draft mode。
我們要做的就是讓他在啟用 Draft Mode 後將網頁導回原本訪問的頁面。
將
/api/draft-mode/enable
導到/redis-%E5%9F%BA%E6%9C%AC%E8%B3%87%E6%96%99%E5%BD%A2%E6%85%8B
所以 enable 的 API 不只要打開 Draft Mode 而已了,還要接收到上面 preview=
的值,並且將請求轉址回到該位置。
分析一下 Sanity 傳給 enable api 的請求:
http://localhost:3000/api/draft-mode/enable?sanity-preview-secret=Msc...asM&sanity-preview-pathname=%2Fredis-%25E5%259F%25BA%25E6%259C%25AC%25E8%25B3%2587%25E6%2596%2599%25E5%25BD%25A2%25E6%2585%258B
可以看到帶過來了兩個 Params 參數,sanity-preview-secret
跟 sanity-preview-pathname
。
現再回到 Next.js 專案稍微修改一下:
import { draftMode } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
// 驗證的部分先跳過
// searchParams.get("sanity-preview-secret")
const redirectUrl = searchParams.get("sanity-preview-pathname") || "/";
draftMode().enable();
return NextResponse.redirect(new URL(redirectUrl, request.url));
}
這樣就得到一個可以跟 Next.js Draft Mode 跟 Sanity Visual Editing 連動的服務了!
現在就剩下最後的問題要解決了: