WordPress 到 Next.js 迁移指南(2026 版)

在过去三年里,我迁移了十多个 WordPress 网站到 Next.js。有些迁移过程很顺利。有些让我在周二凌晨 2 点开始怀疑人生。这两种结果之间的差异几乎总是归结为规划——特别是要理解 WordPress 在实际上为网站做什么,然后才能拆除它。

本指南包含了我希望在第一次迁移前有人交给我的所有内容。我们将涵盖整个过程:评估是否应该迁移,选择无头 CMS,迁移内容,重建模板,在不丧失排名的情况下处理 SEO,以及在 Vercel 上部署一个不会在流量尖峰下崩溃的设置。

让我们开始吧。

WordPress to Next.js Migration on Vercel: A 2026 Guide

目录

为什么在 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、Server Components 和内置缓存提供了 WordPress 无法匹配的现代开发工作流。

规模成本。 托管的 WordPress 主机(WP Engine、Kinsta)对于适度性能需要 30 美元到 300 美元/月。Vercel 的 Pro 计划为 20 美元/用户/月,配合边缘函数和自动扩展,通常成本更低但性能更好。

也就是说——不要只因为流行就迁移。如果你的网站是一个拥有 50 篇文章的简单博客,客户每周通过 WordPress 管理界面更新一次,迁移可能会造成比它解决的问题更多的麻烦。最佳迁移候选者是:

  • 拥有 500+ 页面且需要更好性能的网站
  • 想要基于组件开发的团队
  • 插件冲突造成维护噩梦的网站
  • 达到 WooCommerce 性能限制的电子商务网站
  • 需要在边缘进行 A/B 测试和个性化的营销网站

迁移前审计:WordPress 实际在做什么

这是大多数迁移出错的地方。人们认为他们在迁移一个博客,但 WordPress 实际上在处理联系表单、重定向、图像优化、搜索、评论、身份验证、定时任务和隐藏在插件配置中的十五件其他事情。

在写一行 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 Edge Cache + 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 与 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 to Next.js Migration on Vercel: A 2026 Guide - architecture

选择无头 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 优先交付构建的 CMS。

CMS 定价(2026) 最适合 内容建模 API 类型
Sanity 免费层,Pro 15 美元/用户/月 复杂内容、实时协作 优秀、代码定义 GROQ + GraphQL
Contentful 免费层,300 美元/月 Team 企业、大型团队 良好、UI 定义 REST + GraphQL
Storyblok 免费层,106 欧元/月 Business 可视化编辑、组件 很好、可视化 REST + GraphQL
Strapi v5 免费(自托管),Cloud 从 29 美元/月 完全控制、开源 灵活、UI 定义 REST + GraphQL
Payload CMS 3.0 免费(自托管) 想要代码优先的开发者 优秀、代码定义 REST + GraphQL

如果你的团队在处理迁移,我们通常推荐 Sanity 或 Payload 用于无头 CMS 开发——它们为开发者提供了对内容建模的最大控制权,同时保持编辑的满意度。

选项 3:存储库中的 Markdown/MDX

对于开发者博客和文档网站,将内容存储为 Git 仓库中的 MDX 文件是最简单的方法。无需管理 CMS,无需 API 调用,内容与代码一起版本控制。但这仅在你的内容编辑熟悉 Git 工作流的情况下有效。

内容迁移策略

从 WordPress 导出

WordPress 内置导出(Tools → Export)提供了一个 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,带有 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 的静态生成

对于大多数内容页面,增量静态再生(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 的按需重新验证。大多数无头 CMS 平台支持内容发布时的 webhook 触发。

如果你在看Next.js 开发并想更深入地了解渲染权衡,我们已经单独写过这个内容。

URL 结构和 SEO 保留

这是不可协商的。如果你丧失了 URL 结构,你就丧失了搜索排名。句号。

重定向映射

WordPress 使用像 /2024/03/post-slug//category/term/ 这样的 URL 模式。你的 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,
      },
      // Feed 网址
      {
        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.2 秒 0.8-1.4 秒 快 60-70%
首字节时间 800 毫秒-1.5 秒 50-120 毫秒 快 90%+
累积布局移位 0.12-0.25 0.01-0.05 减少 ~80%
月度托管成本 平均 115 美元/月 20-40 美元/月 节省 60-80%
构建时间(500 页) N/A(动态) 45-90 秒 N/A
页面/秒(ISR) 15-30 请求/秒 来自边缘的 10,000+ 数量级提升

仅 TTFB 改进就值得迁移。WordPress 通过 PHP 和 MySQL 生成每个页面。Vercel 从遍布全球 300 多个位置的边缘节点提供预渲染页面。

常见迁移陷阱

陷阱 1:忘记 WordPress RSS feed。 如果人们订阅你的 feed,将 /feed//rss/ 重定向到新的 RSS 端点。Next.js 默认不生成 feed——你需要一个自定义路由。

陷阱 2:缺少 WordPress 短码。 从 WordPress 导出的内容充满了 [gallery][embed] 和特定插件的短码。如果你不解析和转换这些,它们会呈现为纯文本。为你的内容使用的每个短码类型编写转换器。

陷阱 3:忽视 WordPress 评论数据。 如果你有有价值的评论主题,将它们迁移到 Disqus 或构建自定义评论系统。大多数人只是放弃评论,但先与利益相关者检查。

陷阱 4:不测试内部链接。 WordPress 内容充满了使用绝对 URL 的内部链接(https://yoursite.com/old-path/)。这些需要更新为相对路径或你的新 URL 结构。在迁移期间,简单的正则表达式查找和替换处理大多数情况。

陷阱 5:忘记 wp-content/uploads 引用。 即使在迁移图像后,旧内容可能引用 /wp-content/uploads/2024/03/image.jpg 路径。设置一个 catch-all 重定向或代理这些到你的新图像 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 作为我的 CMS 与 Next.js 一起保留吗? 绝对可以。这被称为"无头 WordPress",这是一种流行的方法。使用 WPGraphQL 将你的内容公开为 API,然后从 Next.js 消费它。你的编辑保留他们了解的 WordPress 管理。主要缺点是你仍然维护 WordPress 安装——安全更新、主机、整个堆栈。

从 WordPress 迁移到 Next.js 成本多少? DIY 由一个开发者:100-300 小时的工作。机构迁移:通常 15,000 美元-75,000 美元,取决于复杂性。持续的托管成本通常会下降——Vercel Pro 为 20 美元/用户/月,相比托管 WordPress 主机的 50 美元-300 美元/月。投资回报来自减少的托管成本、更好的性能(这改进了转化率)和更低的维护开销。

我应该在 Next.js 15 中使用 Pages Router 还是 App Router? App Router,完全。在 2026 年,App Router 是配备 Server Components、流传输和并行路由的稳定默认设置。Pages Router 仍然有效且未弃用,但新功能和优化是 App Router 优先的。我们在 Social Animal 做的每个迁移都独占使用 App Router。查看我们的 Next.js 开发能力了解更多关于我们的方法。

表单和动态功能呢? Next.js API 路由(或 App Router 中的 Server Actions)处理表单提交、搜索、身份验证和任何服务器端逻辑。对于联系表单,你可以使用 Server Actions 与 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 上效果最好。2026 年,Vercel 和替代品之间的 DX 差距已经缩小,但 Vercel 仍然是阻力最小的路径。

如果我需要迁移 WooCommerce 商店怎么办? 这是一个更大的项目。大多数团队将店面迁移到 Next.js,同时将商务后端转移到 Shopify(使用 Storefront API)、Medusa.js 或 Saleor。Shopify 的 Hydrogen 框架是另一个选择,但如果你想完全控制前端,Next.js 与 Shopify API 是最可靠的路径。预期电子商务迁移会向你的时间表添加 4-8 周。