在過去三年中,我遷移了十多個 WordPress 網站至 Next.js。有些進行得很順利。有些讓我在星期二凌晨 2 點質疑我的職業選擇。這兩種結果之間的差異幾乎總是歸結於規劃——具體來說,是在拆除它之前理解 WordPress 對網站的「實際作用」。

本指南涵蓋我希望在首次遷移前就能得到的所有內容。我們將涵蓋完整的旅程:評估你是否應該遷移、選擇無頭 CMS、移動內容、重建模板、處理 SEO 以保持排名,以及在 Vercel 上部署設置,這個設置不會在流量尖峰時崩潰。

讓我們開始吧。

WordPress 轉換至 Next.js 在 Vercel 上的遷移:2026 完整指南

目錄

為什麼在 2026 年從 WordPress 遷移至 Next.js?

讓我們說實話——WordPress 仍然在 2026 年為全球大約 40% 的網站提供支持。它不會消失。但離開的理由已經變得更具吸引力:

效能瓶頸。 即使使用激進的快取外掛(WP Rocket、W3 Total Cache),大多數 WordPress 網站在 Lighthouse 效能評分的 70-80 分左右達到瓶頸。外掛臃腫、渲染阻止 PHP 和每頁加載時的資料庫查詢產生的開銷是任何優化都無法完全消除的。

安全攻擊面。 WordPress 在 2025 年有 149 個文檔化的漏洞,分布在核心和熱門外掛中。每個外掛都是一個攻擊向量。每次主題更新都是潛在的破損。如果你運行 WooCommerce,攻擊面翻倍。

開發者體驗。 如果你的團隊了解 React,用 PHP 模板開發就像用非慣用手寫字一樣。Next.js 15 的 App Router、伺服器元件和內置快取提供了 WordPress 無法匹配的現代開發工作流程。

大規模成本。 受管理的 WordPress 託管(WP Engine、Kinsta)的成本為每月 $30-$300 以獲得良好效能。Vercel 的 Pro 方案每個使用者每月 $20,配合邊緣函數和自動擴展,通常成本更低,效能更好。

也就是說——不要只是因為它流行就遷移。如果你的網站是一個有 50 篇文章的簡單部落格,並且你的客戶每週通過 WordPress 管理員更新它一次,遷移可能會產生比解決更多的問題。最佳遷移候選者是:

  • 超過 500 頁且需要更好效能的網站
  • 想要基於元件開發的團隊
  • 外掛衝突導致維護困難的網站
  • 達到 WooCommerce 效能限制的電商網站
  • 需要邊緣 A/B 測試和個性化的行銷網站

遷移前審計:WordPress 實際在做什麼

這是大多數遷移失敗的地方。人們認為他們在遷移一個部落格,但 WordPress 實際上在處理聯繫表單、重定向、圖像優化、搜尋、評論、身份驗證、cron 工作和十五種其他埋在外掛配置中的東西。

在編寫任何一行 Next.js 代碼之前,請記錄所有內容:

外掛清單

匯出外掛清單並分類每一個:

wp plugin list --status=active --format=csv > active-plugins.csv

對於每個外掛,回答:這個做什麼,在 Next.js 生態系統中什麼替代它?

WordPress 外掛 功能 Next.js 替代品
Yoast SEO 元標籤、網站地圖、架構 next-seo + 自訂 sitemap.xml 路由
WP Rocket 快取、縮小 Vercel 邊緣快取 + Next.js 內置
Contact Form 7 表單處理 React Hook Form + API 路由或 Formspree
Wordfence 安全 無需(無管理員介面)
WPML 多語言 next-intl 或內置 i18n 路由
WooCommerce 電商 Shopify Storefront API 或 Saleor
Advanced Custom Fields 自訂內容模型 你的無頭 CMS 的內容建模
Redirection URL 重定向 next.config.js 重定向或 Vercel 配置
WP Cron 排程工作 Vercel Cron Jobs 或獨立服務
Imagify 圖像優化 next/image with Vercel 圖像優化

內容清單

計算並分類你的內容:

SELECT post_type, post_status, COUNT(*) 
FROM wp_posts 
GROUP BY post_type, post_status;

不要忘記:自訂貼文類型、分類法術語、使用者個人檔案、選單結構、小工具配置和選項值。那個「簡單」的 WordPress 網站可能比你想象的有更多內容類型。

URL 對應

匯出每個 URL。每一個。使用 Screaming Frog 或簡單的網站地圖爬網:

curl -s https://yoursite.com/sitemap_index.xml | \
grep -oP '<loc>\K[^<]+' | \
xargs -I {} curl -s {} | \
grep -oP '<loc>\K[^<]+' > all-urls.txt

這個文件是黃金。你將在重定向對應、SEO 保留和遷移後的 QA 測試中使用它。

WordPress 轉換至 Next.js 在 Vercel 上的遷移:2026 完整指南 - 架構

選擇無頭 CMS 後端

你需要一個地方來儲存和管理內容。2026 年三種最常見的路徑:

選項 1:WordPress 作為無頭 CMS

是的,你可以將 WordPress 保留為後端並使用 Next.js 作為前端。WPGraphQL(現在處於 v2.1)使這出人意料地可行。你的編輯保留熟悉的管理員介面。你得到一個現代的前端。

// lib/wordpress.js
const API_URL = process.env.WORDPRESS_GRAPHQL_URL;

export async function getPostBySlug(slug) {
  const res = await fetch(API_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        query PostBySlug($slug: ID!) {
          post(id: $slug, idType: SLUG) {
            title
            content
            date
            featuredImage {
              node {
                sourceUrl
                altText
              }
            }
            seo {
              title
              metaDesc
              opengraphImage {
                sourceUrl
              }
            }
          }
        }
      `,
      variables: { slug },
    }),
    next: { revalidate: 60 },
  });
  const json = await res.json();
  return json.data.post;
}

缺點?你仍然維護 WordPress 安裝。安全更新、PHP 版本管理、資料庫備份——它全部保留在你的盤子上。你仍然為 WordPress 託管付費。

選項 2:專門建設的無頭 CMS

這是我為大多數遷移推薦的。將你的內容移動到一個從頭開始為 API-first 傳送而構建的 CMS。

CMS 定價 (2026) 最適用於 內容建模 API 類型
Sanity 免費層、$15/user/mo Pro 複雜內容、即時協作 優秀、代碼定義 GROQ + GraphQL
Contentful 免費層、$300/mo Team 企業、大型團隊 良好、UI 定義 REST + GraphQL
Storyblok 免費層、€106/mo Business 視覺編輯、元件 很好、視覺 REST + GraphQL
Strapi v5 免費(自託管)、雲端從 $29/mo 完全控制、開源 靈活、UI 定義 REST + GraphQL
Payload CMS 3.0 免費(自託管) 想要代碼優先的開發者 優秀、代碼定義 REST + GraphQL

如果你的團隊在 Social Animal,我們通常為 無頭 CMS 開發 推薦 Sanity 或 Payload——它們給開發者最多對內容建模的控制,同時保持編輯快樂。

選項 3:儲存庫中的 Markdown/MDX

對於開發者部落格和文檔網站,將內容儲存為 Git 儲存庫中的 MDX 文件是最簡單的方法。無 CMS 要管理、無 API 呼叫、內容與代碼一起版本化。但這只在你的內容編輯者對 Git 工作流程感到舒適時才有效。

內容遷移策略

從 WordPress 匯出

內置的 WordPress 匯出(工具 → 匯出)為你提供一個 XML 文件。這是一個開始,但它很亂。為了進行結構化遷移,我使用自訂 WP-CLI 腳本:

// export-content.php
<?php
$posts = get_posts([
    'post_type' => ['post', 'page', 'your_custom_type'],
    'posts_per_page' => -1,
    'post_status' => 'publish',
]);

$export = [];
foreach ($posts as $post) {
    $export[] = [
        'id' => $post->ID,
        'title' => $post->post_title,
        'slug' => $post->post_name,
        'content' => apply_filters('the_content', $post->post_content),
        'excerpt' => $post->post_excerpt,
        'date' => $post->post_date,
        'modified' => $post->post_modified,
        'author' => get_the_author_meta('display_name', $post->post_author),
        'categories' => wp_get_post_categories($post->ID, ['fields' => 'names']),
        'tags' => wp_get_post_tags($post->ID, ['fields' => 'names']),
        'featured_image' => get_the_post_thumbnail_url($post->ID, 'full'),
        'acf' => function_exists('get_fields') ? get_fields($post->ID) : [],
        'yoast' => [
            'title' => get_post_meta($post->ID, '_yoast_wpseo_title', true),
            'description' => get_post_meta($post->ID, '_yoast_wpseo_metadesc', true),
        ],
    ];
}

file_put_contents('export.json', json_encode($export, JSON_PRETTY_PRINT));

轉換內容

WordPress 內容儲存為 HTML with Gutenberg 區塊標記。你需要決定:保留 HTML 並呈現它,還是轉換為你的 CMS 的結構化格式?

對於 Sanity,我使用 @sanity/block-tools 將 HTML 轉換為 Portable Text。對於 Contentful,他們的遷移 CLI 處理富文本轉換。無論哪種方式,都要為內容清理預算時間——WordPress 內容充滿 [shortcodes]、內聯樣式和需要清理的損壞 HTML。

// migrate-to-sanity.js
import { htmlToBlocks } from '@sanity/block-tools';
import { JSDOM } from 'jsdom';
import { Schema } from '@sanity/schema';

const schema = Schema.compile({ /* your schema */ });
const blockContentType = schema.get('post')
  .fields.find(f => f.name === 'body').type;

function convertPost(wpPost) {
  return {
    _type: 'post',
    title: wpPost.title,
    slug: { current: wpPost.slug },
    publishedAt: wpPost.date,
    body: htmlToBlocks(
      wpPost.content,
      blockContentType,
      { parseHtml: (html) => new JSDOM(html).window.document }
    ),
  };
}

圖像遷移

不要跳過這個。從 wp-content/uploads 下載每張圖像,重新上傳到你的 CMS 或 CDN(Cloudinary、Vercel Blob Storage、S3),並更新所有內容參考。我通常寫一個執行以下操作的腳本:

  1. 爬網匯出 JSON 以查找圖像 URL
  2. 下載每張圖像
  3. 上傳到新儲存
  4. 創建 URL 對應文件
  5. 對所有內容運行尋找和取代

在 Next.js 15 中重建前端

Next.js 15(自 2024 年末穩定,2026 年目前 15.2)默認使用 App Router。以下是我用於內容密集型網站的結構:

app/
├── layout.tsx          # 根配置,包含字體、分析
├── page.tsx            # 首頁
├── blog/
│   ├── page.tsx        # 部落格清單,含分頁
│   └── [slug]/
│       └── page.tsx    # 個別部落格文章
├── [slug]/
│   └── page.tsx        # 通用頁面
├── sitemap.ts          # 動態網站地圖生成
├── robots.ts           # robots.txt
└── not-found.tsx       # 自訂 404

使用 ISR 的靜態生成

對於大多數內容頁面,增量靜態再生是最佳折衷——靜態效能加上動態新鮮感:

// app/blog/[slug]/page.tsx
import { getPostBySlug, getAllPostSlugs } from '@/lib/cms';
import { notFound } from 'next/navigation';

export async function generateStaticParams() {
  const slugs = await getAllPostSlugs();
  return slugs.map((slug) => ({ slug }));
}

export async function generateMetadata({ params }) {
  const post = await getPostBySlug(params.slug);
  if (!post) return {};
  return {
    title: post.seo?.title || post.title,
    description: post.seo?.description || post.excerpt,
    openGraph: {
      images: [post.featuredImage?.url],
    },
  };
}

export const revalidate = 3600; // 每小時重新驗證一次

export default async function BlogPost({ params }) {
  const post = await getPostBySlug(params.slug);
  if (!post) notFound();

  return (
    <article className="prose lg:prose-xl">
      <h1>{post.title}</h1>
      <time dateTime={post.date}>{formatDate(post.date)}</time>
      <PostBody content={post.body} />
    </article>
  );
}

對於需要即時更新的網站(新聞、實時內容),使用來自無頭 CMS 的 webhook 觸發的按需重新驗證。大多數無頭 CMS 平台支持內容發布時的 webhook 觸發。

如果你在查看 Next.js 開發 並想更深入地理解渲染權衡,我們已經單獨寫過。

URL 結構和 SEO 保留

這是不可協商的。如果你丟失你的 URL 結構,你將丟失你的搜尋排名。句點。

重定向對應

WordPress 使用 URL 模式如 /2024/03/post-slug//category/term/。你的 Next.js 網站可能使用 /blog/post-slug。建立重定向對應:

// next.config.js
module.exports = {
  async redirects() {
    return [
      // 基於日期的 URL 到清潔 slug
      {
        source: '/:year(\\d{4})/:month(\\d{2})/:slug',
        destination: '/blog/:slug',
        permanent: true,
      },
      // 分類檔案
      {
        source: '/category/:slug',
        destination: '/blog?category=:slug',
        permanent: true,
      },
      // 分頁
      {
        source: '/page/:num',
        destination: '/blog?page=:num',
        permanent: true,
      },
      // 源 URL
      {
        source: '/feed',
        destination: '/rss.xml',
        permanent: true,
      },
    ];
  },
};

對於大型網站(1000+ 重定向),使用 Vercel 的 vercel.json 配置或中間軟體代替——next.config.js 重定向在大約 1024 個條目後有軟限制,超過此值構建時間開始受影響。

結構化資料

Yoast 等 WordPress 外掛自動生成 JSON-LD。你需要複製此功能:

// components/structured-data.tsx
export function ArticleSchema({ post }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.date,
    dateModified: post.modified,
    author: {
      '@type': 'Person',
      name: post.author,
    },
    image: post.featuredImage?.url,
    publisher: {
      '@type': 'Organization',
      name: 'Your Site Name',
      logo: { '@type': 'ImageObject', url: '/logo.png' },
    },
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

XML 網站地圖

Next.js 15 使動態網站地圖簡單明了:

// app/sitemap.ts
import { getAllPosts, getAllPages } from '@/lib/cms';

export default async function sitemap() {
  const posts = await getAllPosts();
  const pages = await getAllPages();

  return [
    { url: 'https://yoursite.com', lastModified: new Date() },
    ...pages.map((page) => ({
      url: `https://yoursite.com/${page.slug}`,
      lastModified: page.modified,
    })),
    ...posts.map((post) => ({
      url: `https://yoursite.com/blog/${post.slug}`,
      lastModified: post.modified,
    })),
  ];
}

在 Vercel 上部署:實際可行的配置

專案設置

npx create-next-app@latest my-migrated-site --typescript --tailwind --app
cd my-migrated-site
vercel link

環境變數

在 Vercel 的儀表板中設置這些,而不是在提交到 Git 的 .env 文件中:

CMS_API_URL=https://your-cms-api-endpoint
CMS_API_TOKEN=your-read-only-token
REVALIDATION_SECRET=a-random-string-for-webhook-auth
SITE_URL=https://yoursite.com

Vercel 配置

// vercel.json
{
  "crons": [
    {
      "path": "/api/revalidate-sitemap",
      "schedule": "0 */6 * * *"
    }
  ],
  "headers": [
    {
      "source": "/fonts/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    }
  ]
}

按需重新驗證 Webhook

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = request.headers.get('x-revalidation-secret');
  if (secret !== process.env.REVALIDATION_SECRET) {
    return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
  }

  const body = await request.json();
  const { slug, type } = body;

  if (type === 'post') {
    revalidatePath(`/blog/${slug}`);
    revalidatePath('/blog'); // 也重新驗證清單
  } else {
    revalidatePath(`/${slug}`);
  }

  return NextResponse.json({ revalidated: true });
}

將你的 CMS webhook 指向 https://yoursite.com/api/revalidate,帶有密碼頭。現在內容更新在幾秒內出現,無需完整重建。

效能基準:遷移前後

這些是我們在 2025-2026 年在 Social Animal 完成的遷移中的真實數字:

指標 WordPress (WP Engine) Next.js (Vercel) 改進
Lighthouse 效能 62-78 95-100 +30-40%
最大的內容繪製 2.8-4.2s 0.8-1.4s 快 60-70%
首位元組時間 800ms-1.5s 50-120ms 快 90%+
累積配置轉變 0.12-0.25 0.01-0.05 ~80% 減少
月度託管成本 平均 $115/mo $20-40/mo 節省 60-80%
構建時間 (500 頁) N/A (動態) 45-90 秒 N/A
頁面/秒 (ISR) 15-30 req/s 從邊緣 10,000+ 數量級

單是 TTFB 的改進就值得遷移。WordPress 通過 PHP 和 MySQL 動態生成每個頁面。Vercel 從全球 300+ 個位置的邊緣節點提供預先呈現的頁面,延遲為 50-120ms。

常見遷移陷阱

陷阱 1:忘記 WordPress RSS 源。 如果人們訂閱你的源,將 /feed//rss/ 重定向到新的 RSS 端點。Next.js 默認不生成源——你需要自訂路由。

陷阱 2:遺漏 WordPress shortcodes。 從 WordPress 匯出的內容充滿 [gallery][embed] 和外掛特定的 shortcodes。如果你不解析和轉換這些,它們會呈現為純文本。為內容使用的每種 shortcode 類型編寫轉換器。

陷阱 3:忽視 WordPress 評論資料。 如果你有有價值的評論線索,將它們遷移到 Disqus 等服務或構建自訂評論系統。大多數人只是放棄評論,但首先檢查利益相關者。

陷阱 4:未測試內部連結。 WordPress 內容充滿使用絕對 URL 的內部連結(https://yoursite.com/old-path/)。這些需要更新為相對路徑或你的新 URL 結構。在遷移期間,簡單的正則表達式尋找和取代處理大多數情況。

陷阱 5:忘記 wp-content/uploads 參考。 即使遷移圖像後,舊內容也可能參考 /wp-content/uploads/2024/03/image.jpg 路徑。為這些設置全面重定向或將它們代理到你的新圖像 CDN。

如果這感覺令人不知所措,這老實說是正常的。適當的遷移需要 4-12 週,取決於網站複雜性。查看我們的 定價直接聯繫 如果你想要有經驗的人手上的專案。

常見問題

WordPress 到 Next.js 遷移需要多長時間? 對於一個有 100-500 頁的網站,預期 4-8 週的專職開發者。有自訂貼文類型、電商或多語言內容的大型網站需要 8-16 週。內容遷移本身通常佔工作的 20%——其他 80% 是重建模板、處理邊緣情況和 QA 測試每個 URL。

我會在遷移過程中丟失我的 Google 排名嗎? 不會,如果你正確處理重定向。為每個更改的 URL 實現 301 重定向、保留你的元標題和描述、將你的新網站地圖提交給 Google Search Console,如果你的域更改,使用地址更改工具。預期 2-4 週的小排名波動,然後恢復或改進,因為 Google 識別更好的 Core Web Vitals。

我可以將 WordPress 與 Next.js 一起保持作為我的 CMS 嗎? 絕對可以。這被稱為「無頭 WordPress」,這是一種流行的方法。使用 WPGraphQL 將你的內容作為 API 公開,然後從 Next.js 消費它。你的編輯保留他們知道的 WordPress 管理員。主要的缺點是你仍然維護 WordPress 安裝——安全更新、託管、整個堆棧。

從 WordPress 遷移至 Next.js 的成本是多少? DIY with one developer:100-300 小時的工作。代理機構遷移:通常 $15,000-$75,000,取決於複雜性。持續託管成本通常會減少——Vercel Pro 每個使用者每月 $20 對比受管理的 WordPress 託管每月 $50-$300。ROI 來自降低託管成本、更好的效能(提高轉換率)和更低的維護開銷。

我應該在 Next.js 15 中使用 Pages Router 還是 App Router? App Router,全部。在 2026 年,App Router 是穩定的預設值,具有伺服器元件、流和並行路由。Pages Router 仍然有效且未被棄用,但新功能和優化是 App Router 優先。Social Animal 完成的每次遷移都專門使用 App Router。查看我們的 Next.js 開發功能 以深入了解我們的方法。

表單和動態功能怎麼辦? Next.js API 路由(或 App Router 中的伺服器操作)處理表單提交、搜尋、身份驗證和任何伺服器端邏輯。對於聯繫表單,你可以使用伺服器操作配合 Resend 等服務進行電郵傳送。對於搜尋,考慮 Algolia 或 Meilisearch。對於身份驗證,NextAuth.js(現在為 Auth.js v5)涵蓋大多數用例。

Vercel 是 Next.js 託管的唯一選項嗎? 不。你可以在 Netlify、AWS Amplify、Cloudflare Pages 上部署 Next.js,或使用 Node.js 自託管。然而,Vercel 由 Next.js 團隊構建,集成顯示——ISR、邊緣中間軟體、圖像優化和分析都在 Vercel 上表現最佳。Vercel 和替代品之間的 DX 差距在 2026 年已經縮小,但 Vercel 仍然是最小阻力的路徑。

如果我需要遷移 WooCommerce 商店怎麼辦? 這是一個更大的專案。大多數團隊將店面遷移到 Next.js,同時將商務後端移動到 Shopify(使用 Storefront API)、Medusa.js 或 Saleor。Shopify 的 Hydrogen 框架是另一個選項,但如果你想完全控制前端,Next.js 配合 Shopify 的 API 是最經驗豐富的路徑。預期電商遷移會為你的時間表增加 4-8 週。