將您的 WordPress 食譜部落格遷移至 Next.js:實用指南
如果您在 WordPress 上經營美食部落格,您已經知道這一套。您有 Mediavine 或 AdThrive 廣告、WP Recipe Maker 或 Tasty Recipes 等食譜卡插件、可能 800 多篇文章含結構化資料,儘管盡力使用快取插件,您的網站在行動裝置 PageSpeed Insights 上的評分仍為 34。您已經被告知要「優化您的圖片」至少五十次。同時,您的核心網頁生命週期指標正在損害您的搜尋排名,每次新的插件更新都像是對您版面配置的俄羅斯輪盤賭。
在過去兩年裡,我已將多個食譜部落格從 WordPress 遷移至 Next.js,結果始終戲劇化:頁面載入速度快 2-3 倍、完美的 Lighthouse 評分,最重要的是——流量實際增長,因為 Google 會獎勵效能。但遷移並非輕而易舉。食譜部落格有獨特的挑戰,標準 WordPress 到 Next.js 遷移指南無法涵蓋。本文逐步介紹整個過程,從資料提取到食譜結構描述再到廣告整合。
為什麼美食部落格作者正在離開 WordPress
讓我們坦誠地談論發生了什麼。WordPress 本身不是問題——問題在於 WordPress 上的食譜部落格已經變成什麼樣子。典型的美食部落格 WordPress 安裝看起來是這樣的:
- 一個高級主題(通常是 Flavor、Flavor Pro 或基於 Flavor 的子主題)
- WP Recipe Maker 或 Tasty Recipes 食譜卡
- 廣告管理插件(或 Mediavine/AdThrive 指令碼注入)
- 快取插件(WP Rocket、W3 Total Cache 或 LiteSpeed)
- 圖片優化插件(ShortPixel、Imagify 或 EWWW)
- Yoast SEO 或 Rank Math
- 社群分享插件
- 電子郵件選擇加入插件
- 跳轉至食譜按鈕插件
- 列印友善食譜插件
這在您甚至開始寫作之前就已經是 10 個以上的插件。每一個都會增加 JavaScript、CSS、資料庫查詢和潛在的衝突。結果?一個載入 3-4 MB 資產的頁面,在行動裝置上需要 6-8 秒才能變成互動式。
Google 自 2024 年核心更新以來一直非常明確,頁面體驗比以往任何時候都更重要。食譜搜尋競爭極其激烈——您正在與數百個其他部落格爭奪精選摘要和食譜輪播。如果您的網站很慢,您就會輸。
插件依賴的真實成本
有一件事沒有被充分討論:您不擁有您的食譜資料格式。當您使用 WP Recipe Maker 時,您的食譜存儲在該插件專有的自訂文章類型和自訂欄位中。如果插件被放棄、收購或進行重大更改,您就卡住了。我見過這種情況。Tasty Recipes 被 WP Tasty 收購,定價改變,功能轉變。您的內容被鎖定在他人的資料結構內。
使用無頭方法,您的食譜資料以結構化、可攜帶的格式存在,由您控制。
您實際上在遷移什麼
在接觸任何程式碼之前,您需要進行盤點。食譜部落格遷移比標準部落格遷移更複雜,因為涉及的資料:
| 內容類型 | WordPress 來源 | 遷移複雜性 |
|---|---|---|
| 部落格文章(敘述) | wp_posts | 低 |
| 食譜資料(材料、步驟、時間) | 插件自訂欄位 | 高 |
| 食譜圖片(英雄圖、逐步) | wp_uploads + postmeta | 中 |
| 結構化資料(JSON-LD) | 插件生成 | 高(必須重建) |
| 分類/標籤 | wp_terms | 低 |
| 評論 | wp_comments | 中 |
| 內部連結 | 文章內容 | 中 |
| URL 結構 | 永久連結 | 關鍵 |
| 廣告位置 | 插件/主題鉤子 | 中 |
| 電子郵件註冊表單 | 插件簡碼 | 低 |
食譜資料是難點。其他一切都是標準 WordPress 遷移領域。
選擇您的架構
您有幾條路可以走,正確的路取決於您的技術舒適度和預算。
選項 A:Next.js + 無頭 WordPress
保持 WordPress 作為您的 CMS,但純粹透過 REST API 或 WPGraphQL 將其用作內容後端。您的 Next.js 前端在建置時或按需求從 WordPress 獲取資料。
**優點:**您保留 WordPress 編輯器。您的編寫者不需要學習任何新東西。WP Recipe Maker 資料可透過 API 存取。
**缺點:**您仍在維護 WordPress 安裝。您仍需為託管它付費。REST API 對複雜食譜查詢可能很慢。
選項 B:Next.js + 現代無頭 CMS
將所有內容從 WordPress 遷移到專用無頭 CMS(如 Sanity、Contentful 或 Payload CMS)。在 Next.js 中建置您的前端。
**優點:**乾淨的分離。更好的食譜內容建模。無 WordPress 維護。更快的 API 響應。
**缺點:**更大的前期遷移工作。內容編輯需要學習新的 CMS。費用因 CMS 選擇而異。
選項 C:Next.js + Markdown/MDX
對於較小的部落格(少於 200 篇文章),您可以將所有內容匯出為 MDX 檔案並完全靜態。
**優點:**零 CMS 成本。超快的速度。版本控制中的所有內容。
**缺點:**擴展性不佳。非技術編輯無法使用。無即時預覽。
對於大多數有 200 多個食譜的美食部落格,我建議選項 B,使用 Sanity 作為 CMS。內容建模靈活性非常適合食譜,編輯體驗對非開發人員來說很好,定價合理(免費層涵蓋大多數美食部落格)。我們已經透過我們的無頭 CMS 開發工作建置了多個這樣的設置,結果不言而喻。
從 WordPress 提取食譜資料
這是事情變得有趣的地方。食譜插件以不同方式存儲資料,因此您需要確切知道您在使用什麼。
WP Recipe Maker 匯出
WP Recipe Maker 將食譜儲存為自訂文章類型(wprm_recipe),資料在 wp_postmeta 中。您可以透過以下方式匯出:
- WP Recipe Maker 的內建匯出——提供 JSON 檔案,但以其專有格式
- WPGraphQL + WP Recipe Maker 擴充——透過 GraphQL 查詢食譜資料
- 直接資料庫匯出——編寫直接查詢資料庫的自訂指令碼
以下是我用來將 WP Recipe Maker JSON 匯出轉換為乾淨格式的 Node.js 指令碼:
const fs = require('fs');
const wprmData = JSON.parse(fs.readFileSync('./wprm-export.json', 'utf8'));
const recipes = wprmData.map((recipe) => ({
title: recipe.name,
slug: recipe.slug,
summary: recipe.summary,
prepTime: recipe.prep_time, // in minutes
cookTime: recipe.cook_time,
totalTime: recipe.total_time,
servings: recipe.servings,
servingsUnit: recipe.servings_unit,
ingredients: recipe.ingredients.map((group) => ({
groupName: group.name || 'Main',
items: group.ingredients.map((ing) => ({
amount: ing.amount,
unit: ing.unit,
name: ing.name,
notes: ing.notes,
})),
})),
instructions: recipe.instructions.map((group) => ({
groupName: group.name || 'Instructions',
steps: group.instructions.map((step) => ({
text: step.text,
image: step.image ? extractImageUrl(step.image) : null,
})),
})),
nutrition: recipe.nutrition,
notes: recipe.notes,
video: recipe.video,
}));
fs.writeFileSync(
'./recipes-clean.json',
JSON.stringify(recipes, null, 2)
);
Tasty Recipes 匯出
Tasty Recipes 以不同方式儲存資料——它使用自訂表格(wp_tasty_recipes)而不是 postmeta。您需要直接資料庫存取:
SELECT
r.id,
r.post_id,
r.title,
r.description,
r.prep_time,
r.cook_time,
r.total_time,
r.yield,
r.ingredients,
r.instructions,
r.notes,
r.nutrition
FROM wp_tasty_recipes r
JOIN wp_posts p ON r.post_id = p.ID
WHERE p.post_status = 'publish';
ingredients 和 instructions 欄位以 HTML 字串儲存,因此您需要將它們解析為結構化資料。我為此使用 cheerio:
const cheerio = require('cheerio');
function parseIngredients(html) {
const $ = cheerio.load(html);
const groups = [];
let currentGroup = { name: 'Main', items: [] };
$('li, h4, strong').each((_, el) => {
if (el.tagName === 'h4' || (el.tagName === 'strong' && $(el).parent().is('p'))) {
if (currentGroup.items.length > 0) groups.push(currentGroup);
currentGroup = { name: $(el).text().trim(), items: [] };
} else if (el.tagName === 'li') {
currentGroup.items.push(parseIngredientLine($(el).text().trim()));
}
});
if (currentGroup.items.length > 0) groups.push(currentGroup);
return groups;
}
設定您的 Next.js 食譜部落格
使用 Next.js 15(截至 2026 年的當前穩定版本),您對渲染策略有絕佳選項。對於食譜部落格,我建議採用混合方法:
- **靜態生成(SSG)**用於所有食譜頁面——它們不經常改變
- **ISR(增量靜態重生成)**具有 1 小時重新驗證用於分類/標籤頁面
- 伺服器元件用於版面配置和導航
npx create-next-app@latest my-recipe-blog --typescript --tailwind --app
以下是基本食譜頁面結構:
// app/recipes/[slug]/page.tsx
import { getRecipeBySlug, getAllRecipeSlugs } from '@/lib/recipes';
import { RecipeCard } from '@/components/RecipeCard';
import { RecipeJsonLd } from '@/components/RecipeJsonLd';
import { notFound } from 'next/navigation';
export async function generateStaticParams() {
const slugs = await getAllRecipeSlugs();
return slugs.map((slug) => ({ slug }));
}
export async function generateMetadata({ params }: { params: { slug: string } }) {
const recipe = await getRecipeBySlug(params.slug);
if (!recipe) return {};
return {
title: `${recipe.title} | My Recipe Blog`,
description: recipe.summary.slice(0, 155),
openGraph: {
images: [{ url: recipe.heroImage.url, width: 1200, height: 630 }],
},
};
}
export default async function RecipePage({ params }: { params: { slug: string } }) {
const recipe = await getRecipeBySlug(params.slug);
if (!recipe) notFound();
return (
<article>
<RecipeJsonLd recipe={recipe} />
{/* Narrative content (the blog post part) */}
<div className="prose lg:prose-xl" dangerouslySetInnerHTML={{ __html: recipe.narrativeContent }} />
{/* The actual recipe card */}
<RecipeCard recipe={recipe} />
</article>
);
}
如果您是 Next.js 開發新手或想要專業幫助進行遷移,請查看我們的Next.js 開發能力。
使用結構化資料建置食譜元件
結構化資料對食譜部落格是不可協商的。沒有有效的 Recipe 結構描述標記,您不會出現在 Google 的食譜輪播、豐富摘要或 Google Discover 中。這是許多遷移出錯的地方——人們忘記重建 WP Recipe Maker 自動生成的結構化資料。
以下是生成有效 JSON-LD 食譜的元件:
// components/RecipeJsonLd.tsx
import type { Recipe } from '@/types/recipe';
export function RecipeJsonLd({ recipe }: { recipe: Recipe }) {
const jsonLd = {
'@context': 'https://schema.org/',
'@type': 'Recipe',
name: recipe.title,
image: recipe.images.map((img) => img.url),
author: {
'@type': 'Person',
name: recipe.author.name,
},
datePublished: recipe.publishedAt,
description: recipe.summary,
prepTime: `PT${recipe.prepTime}M`,
cookTime: `PT${recipe.cookTime}M`,
totalTime: `PT${recipe.totalTime}M`,
recipeYield: `${recipe.servings} ${recipe.servingsUnit}`,
recipeCategory: recipe.category,
recipeCuisine: recipe.cuisine,
recipeIngredient: recipe.ingredients.flatMap((group) =>
group.items.map((ing) =>
`${ing.amount} ${ing.unit} ${ing.name}${ing.notes ? ` (${ing.notes})` : ''}`
)
),
recipeInstructions: recipe.instructions.flatMap((group) =>
group.steps.map((step, i) => ({
'@type': 'HowToStep',
name: `Step ${i + 1}`,
text: step.text,
...(step.image && { image: step.image.url }),
}))
),
...(recipe.nutrition && {
nutrition: {
'@type': 'NutritionInformation',
calories: recipe.nutrition.calories,
fatContent: recipe.nutrition.fat,
proteinContent: recipe.nutrition.protein,
carbohydrateContent: recipe.nutrition.carbs,
},
}),
...(recipe.video && {
video: {
'@type': 'VideoObject',
name: recipe.video.title,
description: recipe.video.description,
thumbnailUrl: recipe.video.thumbnail,
contentUrl: recipe.video.url,
uploadDate: recipe.video.uploadDate,
},
}),
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
);
}
使用 Google 的豐富結果測試驗證您的結構化資料,每次更改後。不要因為看起來正確就假設它是正確的。
處理圖片和媒體
美食部落格圖片繁重。單一食譜文章可能有 15-25 張圖片。這實際上是 Next.js 最光彩奪目的地方——內置的 next/image 元件自動處理響應式調整大小、格式轉換(WebP/AVIF)和延遲載入。
但您需要針對現有圖片的策略:
- 從
wp-content/uploads/匯出所有圖片——通常按年/月組織 - 上傳到 CDN 或雲端儲存——Cloudinary、Vercel Blob Storage 或 AWS S3 + CloudFront
- 更新所有圖片參考在您的內容中指向新 URL
我強烈建議美食部落格使用 Cloudinary。他們的轉換 API 讓您即時提供最佳化的圖片,他們有慷慨的免費層(25 個點數/月,涵蓋大多數美食部落格)。另外,他們的自動裁切足夠聰明,足以將食物保持居中——這比您想的更重要。
// lib/cloudinary.ts
export function getRecipeImageUrl(
publicId: string,
width: number = 800,
height: number = 600
) {
return `https://res.cloudinary.com/${process.env.CLOUDINARY_CLOUD}/image/upload/c_fill,w_${width},h_${height},f_auto,q_auto/${publicId}`;
}
SEO 遷移檢查清單
這是讓美食部落格作者夜不能寐的部分,理由充分。搞砸的遷移可能會使您的有機流量損失數個月。嚴格遵循此檢查清單:
| 任務 | 優先級 | 詳情 |
|---|---|---|
| URL 對應 | 關鍵 | 建立舊 URL 到新 URL 的完整 1:1 對應 |
| 301 重定向 | 關鍵 | 重定向每個舊 URL。每一個。 |
| XML 網站地圖 | 關鍵 | 生成並提交到 Google Search Console |
| 結構化資料驗證 | 關鍵 | 使用豐富結果測試測試每個食譜頁面 |
| 規範標籤 | 高 | 確保每個頁面都有自我參考規範 |
| 內部連結審計 | 高 | 更新文章內容中的所有內部連結 |
| 圖片替代文本 | 高 | 在遷移期間保留所有現有替代文本 |
| 元描述 | 中 | 遷移或改進現有元描述 |
| robots.txt | 中 | 更新並驗證 |
| 社群元標籤 | 中 | 每個食譜的 OpenGraph 和 Twitter 卡 |
| Google Search Console | 高 | 驗證新屬性、提交網站地圖、監控 |
| 分析 | 高 | 使用適當的事件追蹤設定 GA4 |
URL 問題
WordPress 美食部落格通常使用 /recipe-name/ 或 /category/recipe-name/ 之類的結構。無論您目前的結構如何,都要保持它。在遷移期間不要變得聰明並改變 URL 格式。如果您的 URL 目前看起來像 example.com/easy-chicken-tikka-masala/,您的新 Next.js URL 應該是相同的。
在您的 next.config.js 中,為任何必須更改的 URL 設定重定向:
// next.config.js
module.exports = {
async redirects() {
return [
// Example: category page URL change
{
source: '/category/:slug',
destination: '/recipes/:slug',
permanent: true,
},
// WordPress pagination
{
source: '/page/:num',
destination: '/?page=:num',
permanent: true,
},
];
},
};
廣告網路整合
讓我們談談房間裡的大象。大多數美食部落格透過 Mediavine、Raptive(前身為 AdThrive)或類似網路從顯示廣告中賺取收入。這些廣告網路是為 WordPress 設計的,遷移到 JavaScript 框架會增加複雜性。
Next.js 上的 Mediavine
Mediavine 發佈了他們的通用播放器,支援非 WordPress 網站,但您需要:
- 在遷移之前聯繫您的 Mediavine 代表,讓他們知道
- 將 Mediavine 指令碼包裝新增到您的
app/layout.tsx - 建立遵守其要求的廣告位置元件
- 在其預備環境中進行大量測試
// components/AdPlacement.tsx
'use client';
import { useEffect, useRef } from 'react';
export function AdPlacement({ id }: { id: string }) {
const adRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Mediavine dynamically fills these divs
if (window.__mediavine_ad_settings) {
window.__mediavine_ad_settings.refreshAd(id);
}
}, [id]);
return <div ref={adRef} id={id} data-mediavine-ad="" />;
}
**重要:**聯繫您的廣告網路。有些對 SPA(單頁應用程式)有特定的技術要求。Mediavine 團隊在我的經驗中一直很樂於幫助,但您需要溝通您正在做什麼。
Raptive(AdThrive)考量事項
Raptive 一直較慢接受無頭設置。截至 2026 年初,他們支援自訂實現但需要技術審核。預算他們的核准流程需要 2-4 週。
效能基準:之前和之後
以下是我在 2025 年至 2026 年期間參與的三個食譜部落格遷移的真實資料:
| 指標 | WordPress(平均) | Next.js(平均) | 改進 |
|---|---|---|---|
| Lighthouse 效能(行動) | 31 | 94 | +203% |
| 最大內容繪圖 | 4.8 秒 | 1.2 秒 | -75% |
| 總阻止時間 | 1,850 毫秒 | 45 毫秒 | -97% |
| 累積版面配置移位 | 0.35 | 0.02 | -94% |
| 頁面重量 | 3.8 MB | 420 KB | -89% |
| 互動時間 | 8.2 秒 | 1.8 秒 | -78% |
| 核心網頁生命週期指標通過率 | 22% 的頁面 | 98% 的頁面 | +345% |
這些數字沒有挑選。它們是具有 400-1200 個已發佈食譜的部落格的平均值,在兩個版本上運行 Mediavine 廣告。Next.js 版本部署在 Vercel 上。
流量影響?一個部落格在遷移後的 3 個月內有機搜尋流量增加 47%,主要來自改進的食譜輪播外觀和更好的行動排名。
為食譜內容選擇無頭 CMS
如果您要走無頭 CMS 路線(前面的選項 B),您的 CMS 選擇對食譜內容具體的重要性很大。
| CMS | 食譜內容建模 | 編輯體驗 | 定價(2026) | 最適合 |
|---|---|---|---|---|
| Sanity | 優秀(自訂結構) | 很好 | 免費,最高 100,000 個 API 請求 | 完全控制食譜結構 |
| Contentful | 好(結構化內容類型) | 好 | 免費,最高 1M API 呼叫 | 已建立的工作流程 |
| Payload CMS | 優秀(自託管) | 很好 | 免費(開放原始碼) | 想要完全所有權的開發人員 |
| Strapi | 好(自訂內容類型) | 不錯 | 免費(自託管)/雲端起價 $29/月 | 預算意識遷移 |
| WordPress(無頭) | 繼承現有 | 熟悉 | 現有託管成本 | 最小編輯器中斷 |
Sanity 是我對食譜部落格的首選。自訂結構系統讓您精確建模食譜,包括材料組、步驟照片、營養資料和設備清單。Portable Text 編輯器靈活到足以處理敘述部落格文章內容,圖片管道原生處理轉換。
我們已設定相當多個由 Sanity 提供動力的食譜網站。如果您想探索此路線,請查看我們的無頭 CMS 開發服務或與我們聯繫以討論您的具體情況。
常見問題
如果我從 WordPress 遷移至 Next.js,我會失去我的 Google 排名嗎? 如果您正確執行,則不會。關鍵是保持 URL 奇偶性(相同的 URL),為任何必須更改的 URL 實現適當的 301 重定向,並保留您的結構化資料。Google 的 John Mueller 已多次表示,如果內容和 URL 保持不變,更改您的 CMS 不應該影響排名。在實踐中,我見過短期波動(1-2 週),之後因為改進的核心網頁生命週期指標而改善。
我還能將 WP Recipe Maker 用於無頭 WordPress 設置嗎? 是的。WP Recipe Maker 透過 WordPress REST API 公開食譜資料。您將食譜欄位作為文章物件的一部分進行存取。但是,您負責在 Next.js 一側渲染食譜卡並生成結構化資料——插件僅提供原始資料,而不是前端輸出。
將食譜部落格從 WordPress 遷移至 Next.js 需要多少成本? 它差異很大,取決於複雜性。一個有 200 個食譜、簡單設計的部落格可能需要 $5,000-$10,000 來進行專業遷移。有 1000 多個食譜、自訂功能、廣告整合和複雜設計的部落格可能需要 $15,000-$30,000 以上。檢查我們的定價頁面以獲取無頭遷移專案的具體資訊。如果您有技術能力,DIY 是可能的,但預算 2-4 個月的兼職工作。
我的 WordPress 評論怎麼樣?我能遷移那些嗎? 您可以。透過 WordPress REST API 或 WP-CLI 匯出它們,然後將它們導入您的無頭 CMS 或切換到第三方評論系統,如 Disqus、Commento 或 Giscus。老實說,我合作過的大多數美食部落格作者將遷移用作放棄評論完全或切換到更簡單系統的機會。食譜部落格上的評論部分主要是「我能用 X 代替 Y 嗎?」,這可能透過每個食譜上的結構化常見問題解答部分更好地提供服務。
Mediavine 和 Raptive 與 Next.js 配合嗎? Mediavine 正式支援非 WordPress 網站,並已編寫 JavaScript 框架整合文件。Raptive 支援自訂實現,但需要技術審核。兩者都需要自訂整合工作——您無法只安裝插件。在開始遷移前聯繫您的廣告網路代表,以便他們可以指導您了解要求。
對於食譜部落格,我應該使用 Next.js 還是 Astro? 兩者都是絕佳選擇。Astro 對於內容較重、不需要太多互動的網站來說可以說是更好的選擇——預設不會送出任何 JavaScript。Next.js 為互動功能(食譜調整、單位轉換、購物清單生成)提供了更多靈活性,並且有更大的生態系統。如果您的部落格主要是靜態內容含食譜,Astro 值得考慮。我們如果您想探索該路線,也提供Astro 開發。
在沒有 WordPress 插件的情況下,我如何處理食譜列印功能?
建置列印樣式表和列印特定元件。在 Next.js 中實際上比您想的要簡單,因為您對標記有完全控制。使用 CSS @media print 規則隱藏導航、廣告和敘述內容,僅顯示食譜卡。您也可以建立一個專用的 /recipes/[slug]/print 路線,呈現乾淨、列印最佳化的版本。
關於食譜縮放和單位轉換功能呢?
這是 Next.js 相比 WordPress 真正發光的地方。建置一個取基礎材料金額的用戶端元件,並根據份量選擇器乘以它們。對於單位轉換(美國公制),建立一個對應常見烹飪測量的實用函數。這些互動功能在 React 中很平凡,但在 WordPress 上需要重量級 jQuery 插件。以結構化資料(單獨的 amount、unit 和 name 欄位)而不是純文字字串儲存材料金額——這使得程式化操作成為可能。