跟上最新開發潮流!學習如何用 Next.js + Headless Ghost CMS,並導入 AI 工具,讓你的 Headless 主題開發效率最大化。實戰教學都在這!
前言:為何選擇 Next.js 搭配 Headless Ghost CMS?
在現代網頁開發實踐中,Headless CMS 架構提供了前所未有的靈活性與效能。將 Ghost CMS 作為後端內容管理系統,搭配如 Next.js 這樣強大的前端框架,可以讓開發團隊建構出速度極快、易於擴展且高度客製化的網站應用。Ghost CMS 以其簡潔易用的編輯介面和強大的內容 API 聞名,而 Next.js 則提供了伺服器端渲染 (SSR)、靜態網站生成 (SSG)、增量靜態再生 (ISR) 等多種渲染模式,以及優化的開發體驗。部署在 Vercel 平台上,更能享受其全球 CDN 加速、自動化部署等優勢。然而,從傳統的 Ghost 主題(基於 Handlebars 模板引擎)遷移到 Next.js(基於 React/JSX)需要進行架構和程式碼層面的轉換,這正是 AI 工具可以大顯身手之處。
理解轉換的核心挑戰:從 Handlebars 到 React/JSX
Ghost 主題的核心是 Handlebars 模板語言(.hbs
檔案)。Handlebars 提供了一套邏輯簡單的模板語法,用於將後端數據(如文章標題、內容、作者資訊等)渲染到 HTML 結構中。它包含內建的 Helper Functions(例如 {{#foreach posts}}
、{{title}}
、{{content}}
),這些函數直接與 Ghost 後端緊密整合。
相對地,Next.js 使用 React 作為其 UI 庫,模板則是以 JSX(JavaScript XML)語法編寫。JSX 允許開發者在 JavaScript 程式碼中直接編寫類似 HTML 的結構。數據的獲取和渲染邏輯與 Handlebars 完全不同。在 Next.js 中,我們通常需要透過 Ghost Content API 非同步地獲取數據,然後在 React 組件中處理這些數據,並使用 JSX 將其渲染出來。
主要轉換挑戰包括:
- 語法轉換:將 Handlebars 的模板語法和 Helper Functions 轉換為等效的 JSX 結構和 React 組件邏輯。
- 數據獲取:從依賴 Ghost 自動注入數據,轉變為在 Next.js 中明確使用 Ghost Content API 進行數據請求。
- 狀態管理:React 組件可能需要管理自身狀態,這在 Handlebars 中是不存在的概念。
- 路由機制:Next.js 擁有基於檔案系統的路由,需要將 Ghost 的路由概念(如
/post/:slug
)映射到 Next.js 的pages
或app
目錄結構。 - 樣式遷移:將主題中的 CSS 或 SCSS 樣式整合到 Next.js 的樣式方案中(如 CSS Modules, Tailwind CSS, Styled Components 等)。
選擇合適的 AI 工具:Cursor 與 Claude Sonnet 3.7
利用 AI 工具可以顯著加速這一轉換過程,特別是在處理重複性的語法轉換任務時。
Cursor:整合式開發環境的 AI 輔助
Cursor 是一個基於 VS Code 的 AI 原生程式碼編輯器。它的優勢在於深度整合了 AI 能力到開發流程中:
- 程式碼生成與轉換:您可以直接選取一段 Handlebars 程式碼,要求 Cursor 將其轉換為 React/JSX 組件。
- 上下文感知:Cursor 能理解整個專案的上下文,有助於生成更貼合現有程式碼風格和結構的程式碼。
- 即時問答與除錯:可以直接在編輯器中提問關於 Next.js、React 或 Ghost API 的問題,甚至讓它協助除錯。
- 整合終端與版本控制:在同一個環境中完成程式碼編輯、測試和版本提交。
使用 Cursor 進行轉換,就像擁有一位隨時待命的 AI 程式設計夥伴。
Claude Sonnet 3.7:強大的自然語言處理與程式碼生成
Claude 系列模型,特別是像 Sonnet 3.7 這樣的新版本,擁有強大的自然語言理解和程式碼生成能力。您可以透過其 Web 介面或 API 使用:
- 批量轉換:可以將整個
.hbs
檔案的內容貼給 Claude,並要求它生成對應的 Next.js 頁面或組件的 JSX 程式碼。 - 複雜邏輯解釋與重構:對於 Handlebars 中較複雜的 Helper 或邏輯,可以要求 Claude 解釋其功能,並提出在 React 中實現的建議方案。
- API 整合指導:可以詢問 Claude 如何在 Next.js 中使用 Ghost Content API 獲取特定數據,並提供程式碼範例。
- 靈活性:不受特定編輯器限制,可以在任何支援複製貼上的環境中使用。
Claude 更像是一位知識淵博的顧問,能夠處理更大塊的文本和更複雜的指令。
選擇建議:對於需要緊密結合開發流程、逐步迭代轉換的場景,Cursor 可能是更佳選擇。若您需要處理大量檔案的初步轉換,或需要針對特定複雜邏輯進行深入探討,Claude 的能力可能更為突出。實務上,兩者結合使用往往能達到最佳效果。
步驟一:準備您的 Ghost 主題檔案
在開始轉換之前,請確保您擁有完整的 Ghost 主題檔案。這通常是一個包含以下內容的 .zip
檔案或資料夾:
.hbs
檔案:位於根目錄或partials/
子目錄下,定義了網站的各個頁面和組件結構(如index.hbs
,post.hbs
,page.hbs
,partials/loop.hbs
等)。package.json
:定義主題的元數據和依賴。assets/
資料夾:包含 CSS、JavaScript、圖片、字體等靜態資源。- 其他可能的設定檔或 Helper 檔案。
請先解壓縮主題檔案,熟悉其結構和各個 .hbs
檔案的功能。
步驟二:設定 Next.js 專案環境
我們需要一個新的 Next.js 專案作為轉換目標。打開您的終端機,執行以下命令:
npx create-next-app@latest my-headless-ghost-frontend
cd my-headless-ghost-frontend
這將創建一個名為 my-headless-ghost-frontend
的新 Next.js 專案。您可以選擇使用 TypeScript 或 JavaScript,以及是否使用 App Router 或 Pages Router(對於從傳統主題遷移,Pages Router 可能稍微直觀一些,但 App Router 是未來趨勢)。
接著,創建一個基本的資料夾結構來組織您的組件和樣式,例如:
my-headless-ghost-frontend/
├── components/ # React 組件
├── pages/ # Next.js 頁面 (如果使用 Pages Router)
│ ├── index.js
│ └── [slug].js # 動態路由範例
├── app/ # Next.js 路由 (如果使用 App Router)
├── public/ # 靜態資源 (圖片、字體等)
├── styles/ # 全域樣式和 CSS Modules
├── lib/ # 放置 API 客戶端等輔助程式碼
├── node_modules/
├── package.json
└── next.config.js
步驟三:使用 AI 進行 Handlebars 到 JSX 的轉換
這是轉換的核心步驟。我們將以 index.hbs
(通常是首頁模板)和 post.hbs
(文章頁模板)為例。
範例:轉換 index.hbs
到 pages/index.js
(或 app/page.js
)
-
複製 Handlebars 程式碼:打開您的
index.hbs
檔案,複製其內容。 -
使用 AI 工具:
- 在 Cursor 中:打開
pages/index.js
(或app/page.js
),選取您想要替換的區塊(或整個檔案),按下Cmd+K
(或Ctrl+K
),輸入類似以下的指令:“將以下 Handlebars 程式碼轉換為一個 Next.js React 組件,用於首頁。假設文章數據會透過 props 傳入一個名為
posts
的陣列。保留 HTML 結構和 CSS class。將 Handlebars helpers 如{{#foreach posts}}
,{{title}}
,{{url}}
,{{excerpt}}
轉換為對應的 JSX 和 JavaScript 邏輯。”{{!< default}} {{!-- The main content area --}} <main id="site-main" class="site-main outer"> <div class="inner posts"> <div class="post-feed"> {{#foreach posts}} {{> "post-card"}} {{!-- A partial --}} {{/foreach}} </div> </div> </main>
- 使用 Claude:打開 Claude 介面,貼上類似以下的指令和 Handlebars 程式碼:
“請將以下 Ghost CMS 的 Handlebars 模板程式碼 (
index.hbs
) 轉換為一個 Next.js 的 React 功能組件 (pages/index.js
)。此組件應接收一個名為posts
的 props 陣列,其中包含文章列表數據。請將{{#foreach posts}}
循環轉換為.map()
函數,並將{{> "post-card"}}
部分假設為一個名為PostCard
的 React 組件(您不需要生成PostCard
的程式碼,只需顯示如何調用它並傳遞必要的 props,如post
物件)。保留原有的 HTML 結構和 CSS class 名稱。”{{!-- (貼上 index.hbs 內容) --}}
- 在 Cursor 中:打開
-
審查與修改 AI 生成的程式碼:AI 會生成類似以下的 JSX 程式碼:
// pages/index.js (或 app/page.js) import Head from 'next/head'; import Layout from '../components/Layout'; // 假設您有一個 Layout 組件 import PostCard from '../components/PostCard'; // 引入 PostCard 組件 // 假設 getStaticProps 或 getServerSideProps 會傳入 posts export default function Home({ posts }) { return ( <Layout> <Head> <title>我的 Next.js Ghost 博客</title> {/* 其他 Head 元素 */} </Head> <main id="site-main" className="site-main outer"> <div className="inner posts"> <div className="post-feed"> {posts && posts.map((post) => ( // 將單個 post 數據傳遞給 PostCard 組件 <PostCard key={post.id} post={post} /> ))} </div> </div> </main> </Layout> ); } // 您還需要添加 getStaticProps 或 getServerSideProps 來獲取 posts 數據 // ... (見步驟四)
-
處理 Partial (
{{> "partial-name"}}
):Handlebars 中的 Partial 對應 React 中的可重用組件。對於{{> "post-card"}}
,您需要:- 找到
partials/post-card.hbs
檔案。 - 使用 AI 工具將其內容轉換為一個新的 React 組件,例如
components/PostCard.js
。 - 確保
PostCard
組件接收必要的 props(例如,一個包含單篇文章數據的post
物件)。 - 在父組件(如
pages/index.js
)中導入並使用PostCard
組件。
- 找到
-
重複此過程:對
post.hbs
(轉換為pages/[slug].js
或app/posts/[slug]/page.js
)、page.hbs
以及其他模板和 Partial 進行相同的轉換。
處理 Ghost Helper Functions
這是轉換中最需要注意的部分。Handlebars Helpers 需要被 React 邏輯和 API 數據替換:
{{title}}
,{{content}}
,{{slug}}
,{{published_at}}
等:這些通常直接對應從 Ghost Content API 獲取的數據欄位。例如,{{title}}
變為{post.title}
。{{@site.title}}
,{{@site.logo}}
:這些是全站設定,需要透過 API 的settings
端點獲取,並可能儲存在 React Context 或作為 props 傳遞。{{#foreach collection}}...{{/foreach}}
:轉換為 JavaScript 的.map()
方法來迭代陣列。{{#if condition}}...{{else}}...{{/if}}
:轉換為 JSX 中的條件渲染({condition && ...}
或{condition ? ... : ...}
)。{{content}}
:API 返回的html
欄位通常可以直接使用dangerouslySetInnerHTML={{ __html: post.html }}
渲染,但請注意 XSS 風險,確保信任內容來源。或者,考慮使用 Markdown 解析庫將mobiledoc
或markdown
格式的內容轉換為 React 組件。{{url}}
:轉換為 Next.js 的路由連結,可能使用<Link href={/posts/${post.slug}}>
。- 日期格式化 (
{{date format="..."}}
):使用date-fns
或dayjs
等 JavaScript 庫在前端進行格式化。 - 分頁 (
{{pagination}}
):需要自己實現分頁邏輯,透過 API 的page
和limit
參數獲取不同頁面的數據,並在前端渲染分頁控件。
您可以向 AI 工具提問如何轉換特定的 Handlebars Helper。例如:“如何在 Next.js React 組件中實現類似 Ghost Handlebars {{date format="DD MMM YYYY"}}
的功能,假設我有一個 ISO 格式的日期字串 post.published_at
?”
迭代與修正:AI 生成程式碼的驗證
AI 生成的程式碼很少能一次完美。 您需要:
- 仔細審查:檢查 HTML 結構、CSS class 是否正確,邏輯是否符合預期。
- 運行與測試:在本地運行 Next.js 開發伺服器 (
npm run dev
),瀏覽頁面,檢查是否有錯誤或渲染問題。 - 逐步除錯:使用瀏覽器開發者工具和
console.log
來追蹤數據流和組件狀態。 - 向 AI 提問:如果遇到問題,可以將錯誤訊息或有問題的程式碼片段貼給 AI,請求協助除錯或提供替代方案。
步驟四:整合 Ghost Content API
Headless 架構的核心是透過 API 獲取內容。您需要在 Next.js 應用中設置 Ghost Content API 客戶端。
安裝 Ghost Content API JavaScript SDK
npm install @tryghost/content-api
在 Next.js 中獲取數據 (getStaticProps
/ getServerSideProps
)
在需要從 Ghost 獲取數據的頁面(例如 pages/index.js
或 pages/[slug].js
)中,使用 Next.js 的數據獲取函數。
創建一個 API 客戶端實例(例如,在 lib/ghost.js
):
// lib/ghost.js
import GhostContentAPI from '@tryghost/content-api';
const api = new GhostContentAPI({
url: process.env.GHOST_URL, // 從環境變數讀取 Ghost 站點 URL
key: process.env.GHOST_CONTENT_API_KEY, // 從環境變數讀取 Content API Key
version: "v5.0" // 使用最新的穩定 API 版本
});
export async function getPosts() {
return await api.posts
.browse({
limit: "all", // 或設置分頁
include: 'tags,authors' // 包含關聯數據
})
.catch(err => {
console.error(err);
return [];
});
}
export async function getSinglePost(postSlug) {
return await api.posts
.read({
slug: postSlug,
include: 'tags,authors'
})
.catch(err => {
console.error(err);
return null;
});
}
export async function getSettings() {
return await api.settings
.browse()
.catch(err => {
console.error(err);
return {};
});
}
// 可以根據需要添加更多獲取函數 (例如 getTags, getAuthors, getPages)
在頁面組件中使用 getStaticProps
(用於 SSG,推薦用於部落格) 或 getServerSideProps
(用於 SSR):
// pages/index.js
import { getPosts, getSettings } from '../lib/ghost';
// ... (其他 import 和 Home 組件)
export async function getStaticProps() {
const posts = await getPosts();
const settings = await getSettings(); // 獲取全站設定
if (!posts) {
return {
notFound: true,
};
}
return {
props: { posts, settings }, // 將數據傳遞給 Home 組件
revalidate: 60, // 可選:啟用 ISR,每 60 秒重新生成頁面
};
}
// pages/[slug].js
import { getSinglePost, getPosts, getSettings } from '../lib/ghost';
import Head from 'next/head';
import Layout from '../../components/Layout'; // 假設路徑
export default function PostPage({ post, settings }) {
if (!post) return <div>文章未找到</div>; // 或顯示 404 組件
return (
<Layout settings={settings}>
<Head>
<title>{post.title}</title>
{/* 添加 meta description, open graph tags 等 */}
<meta name="description" content={post.excerpt} />
</Head>
<article>
<h1>{post.title}</h1>
{/* 其他作者、日期等資訊 */}
<div
className="post-content" // 確保樣式能應用
dangerouslySetInnerHTML={{ __html: post.html }}
/>
</article>
</Layout>
);
}
export async function getStaticPaths() {
const posts = await getPosts();
const paths = posts.map((post) => ({
params: { slug: post.slug },
}));
// fallback: 'blocking' 或 true 或 false,決定未預先生成的路徑行為
return { paths, fallback: 'blocking' };
}
export async function getStaticProps({ params }) {
const post = await getSinglePost(params.slug);
const settings = await getSettings();
if (!post) {
return {
notFound: true,
};
}
return {
props: { post, settings },
revalidate: 60, // ISR
};
}
將 API 數據傳遞給組件
如上例所示,透過 getStaticProps
或 getServerSideProps
返回的 props
物件會自動傳遞給頁面組件。然後您可以將這些數據進一步傳遞給子組件。
步驟五:樣式與靜態資源遷移
CSS/SCSS 處理
- 全域樣式:將原主題的主要 CSS 檔案(例如
assets/css/screen.css
)複製到 Next.js 的styles
目錄下,並在pages/_app.js
(或app/layout.js
) 中導入:import '../styles/globals.css';
。 - 組件級樣式:如果您的 Ghost 主題 CSS 結構良好,您可以考慮將其拆分為 CSS Modules (
.module.css
),或者如果您熟悉 Tailwind CSS,可以考慮使用 Tailwind 重寫樣式,這通常能提供更好的維護性和效能。AI 工具也可以協助進行 CSS 到 Tailwind class 的轉換。 - SCSS/Sass:如果您的主題使用 SCSS,需要安裝
sass
依賴 (npm install sass
),Next.js 會自動支持.scss
或.sass
檔案的導入。
圖片與其他靜態檔案
-
公共資源:將主題
assets/
文件夾中的圖片、字體等靜態資源複製到 Next.js 的public/
目錄下。這些檔案可以透過根路徑直接訪問(例如public/images/logo.png
可透過/images/logo.png
訪問)。 -
Next.js Image 組件:為了獲得更好的效能(自動圖片優化、延遲加載等),建議使用 Next.js 的
<Image>
組件來顯示圖片,而不是標準的<img>
標籤。您需要將<img>
標籤轉換為<Image>
。import Image from 'next/image'; // ... // 替換 <img src="/images/logo.png" alt="Logo"> <Image src="/images/logo.png" // 相對於 public 目錄的路徑 alt="Logo" width={500} // 需要提供寬高 height={150} // layout="responsive" // 或其他 layout 選項 // priority // 如果是 LCP 元素 />
-
來自 Ghost 的圖片:對於從 Ghost API 獲取的圖片 URL(例如
feature_image
),您可能需要在next.config.js
中配置images.domains
或images.remotePatterns
以允許 Next.js Image 組件優化來自 Ghost 伺服器的圖片。// next.config.js module.exports = { images: { remotePatterns: [ { protocol: 'https', // 或 'http' hostname: 'your-ghost-domain.com', // 您的 Ghost 站點域名 port: '', pathname: '/content/images/**', // 通常是這個路徑 }, ], }, }
步驟六:部署至 Vercel
將您的 Headless Ghost CMS 前端部署到 Vercel 非常簡單。
-
推送程式碼到 Git 倉庫:確保您的 Next.js 專案已經初始化 Git 並推送到 GitHub, GitLab, 或 Bitbucket。
-
在 Vercel 上創建新專案:登入您的 Vercel 帳戶,選擇 “Add New…” → “Project”。
-
導入 Git 倉庫:選擇您剛剛推送的 Git 倉庫。
-
配置專案:
- Framework Preset:Vercel 通常會自動檢測到是 Next.js 專案。
- Build and Output Settings:通常保留預設即可。
- Environment Variables:這是關鍵步驟。您需要添加之前在
lib/ghost.js
中使用的環境變數:GHOST_URL
:您的 Ghost 站點的公開 URL (例如https://your-ghost-blog.com
)。GHOST_CONTENT_API_KEY
:您在 Ghost 後台 Integrations 頁面創建的 Content API Key。
-
部署:點擊 “Deploy” 按鈕。Vercel 將自動拉取程式碼、安裝依賴、構建專案並部署。後續每次推送到主分支(或您配置的生產分支)時,Vercel 都會自動觸發新的部署。
AI 轉換的注意事項與最佳實踐
- AI 不是魔法:將 AI 視為加速器和助手,而非完全自動化的解決方案。始終需要人工審查、測試和調整。
- 提供清晰的指令:給 AI 的指令越具體、上下文越豐富,生成的結果就越好。說明輸入(Handlebars)、輸出期望(React/JSX)、數據來源(props, API)、以及任何特殊要求(保留 class, 使用特定庫)。
- 分而治之:不要試圖一次轉換整個龐大的主題。從小的 Partial 或簡單的頁面模板開始,逐步構建。
- 理解基礎:即使使用 AI,對 React, Next.js, 和 Ghost API 的基本理解仍然是必要的,這有助於您判斷 AI 生成的程式碼是否合理,以及如何進行修改。
- 性能考量:AI 可能不會自動優化程式碼。注意 React 組件的渲染性能、避免不必要的重新渲染、合理使用
React.memo
,useCallback
,useMemo
等。確保 Next.js 的數據獲取策略(SSG, ISR, SSR)符合您的需求。 - 可訪問性 (Accessibility):檢查轉換後的 HTML 結構是否仍然符合無障礙標準。
總結:擁抱 AI 加速 Headless CMS 開發
將 Ghost CMS 主題轉換為 Next.js 模板以實現 Headless 架構是一項有價值的投資,能帶來顯著的性能提升和開發靈活性。雖然這個過程涉及不同技術棧之間的轉換,但藉助 Cursor 或 Claude Sonnet 3.7 等先進的 AI 工具,可以大大簡化和加速許多重複性的程式碼轉換任務。遵循本文概述的步驟,從準備主題、設置 Next.js 環境、利用 AI 進行核心轉換、整合 Ghost Content API,到樣式遷移和 Vercel 部署,您將能夠成功地將您的 Ghost 內容以現代化、高性能的方式呈現給用戶。記住,AI 是強大的輔助,結合您的專業知識和細緻的測試,定能打造出卓越的 Headless Ghost CMS 網站。
Tenten.co:您的數位轉型夥伴
在進行如 Ghost CMS 到 Headless Next.js 這類複雜的技術遷移或尋求更廣泛的數位解決方案時,擁有一個經驗豐富的技術夥伴至關重要。Tenten.co 是一家專注於提供頂尖網站設計、開發、SEO 優化及數位策略的專業機構。我們的團隊精通各種現代網頁技術,包括 Headless CMS 架構、Next.js 開發、AI 應用整合以及 Vercel 部署優化。我們致力於幫助客戶實現數位轉型目標,打造兼具美觀、性能與商業價值的線上平台。如果您在轉換過程中遇到挑戰,或希望進一步提升您的數位體驗,歡迎與我們聯繫。