iT邦幫忙

2024 iThome 鐵人賽

DAY 28
0

前一篇設定了 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

Next.js 的 Draft Mode 主要是解決在跟 headless CMS 平台整合開發時,有時候有新內容必須要發佈時,必須要發佈才能在 Next.js 的專案內看到,或是有些按鈕、功能只有在 Draft Mode 中才要啟用。

開關 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。

https://ithelp.ithome.com.tw/upload/images/20241012/201019890j38UniVMZ.jpg

要關閉的話也很簡單,只要簡單的使用 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 後再訪問該頁面,像是要訪問這頁:

https://ithelp.ithome.com.tw/upload/images/20241013/20101989uqA3TTE3tT.jpg

就會在訪問這頁之前先去對 /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-secretsanity-preview-pathname

  • 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 連動的服務了!


現在就剩下最後的問題要解決了:

  • 驗證 Sanity sanity-preview-secret
  • 編輯的內容不會即時響應在前端畫面上
  • Draft 文章 ( 尚未 published 的文章 ) 也要能顯示在 Draft Mode

上一篇
Day 27 - Sanity Visual Editing
下一篇
Day 29 - Next.js 與 Sanity 的 Security 驗證
系列文
用 Sanity 跟 Nextjs 重寫個人部落格30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言