Next.js vs Astro 之 SEO 对决(2026):你该选哪个?
一篇务实、不吹捧的 Next.js 与 Astro SEO 对比 —— 渲染、性能、i18n,以及各自的取胜场景。
- Next.js
- Astro
- Comparison
Next.js 和 Astro 都能排得很好。无论用哪个框架,Google 都会毫无怨言地索引服务端渲染出的 HTML。如果你把「哪个框架对 SEO 更好」理解成「Google 暗地里偏爱哪个」,那就问错了问题。正确的问题更具体、也更有用:就你正在构建的这类站点而言,哪个框架能让做好 SEO 成为阻力最小的那条路?
这是一篇面向开发者的对比。我们会看真实的渲染管线、默认情况下会有什么被发送到浏览器、Core Web Vitals 最终如何,以及每个框架如何处理 hreflang 和结构化数据这类不光鲜却起决定作用的活儿。不搞门派之争 —— 只谈权衡与代码。
TL;DR —— 一句话结论
如果你在做内容站或营销站(博客、文档、落地页、程序化 SEO),Astro 开箱即给你近乎零 JS 的 HTML,让你几乎没有机会不小心把 Core Web Vitals 搞砸。如果你在做一个外挂了内容层的交互式应用(仪表盘、带营销页面的 SaaS、带大量个性化的电商),Next.js 让整个应用共用一套心智模型,并为必须动态的页面提供成熟的 SSR/ISR。
| 维度 | Astro | Next.js(App Router) |
|---|---|---|
| 默认渲染 | SSG(预渲染 HTML) | SSR / RSC(按请求服务端渲染) |
| 默认发往浏览器的 JS | ~0 KB(仅 islands) | React 运行时 + RSC payload |
| 动态渲染 | SSR 适配器,按需 | SSR、ISR、流式、RSC —— 一等公民 |
| 默认 Core Web Vitals | 优秀(几乎没东西要 hydrate) | 良好,但容易退化 |
| i18n | 内置路由 + 手写 hreflang | i18n 配置(Pages)/ 手写(App)+ hreflang |
| 结构化数据 | 内联在 .astro 模板中 | 内联在组件 / generateMetadata 中 |
| 最适合 | 内容站与营销站 | 带内容的交互式应用 |
| 学习曲线 | 低(会 HTML/JS 即可) | 中等(RSC 心智模型) |
两种选择都不会带来排名惩罚。差别在于:你需要付出多少警觉,才能让页面保持快速和可抓取。
🧑💻 开发者视角:框架本身很少会让你掉排名。真正让你掉排名的,是为渲染一段文字就发送 400 KB 的 JS 包,或者明明只需要一个交互式小组件却 hydrate 了整个页面。Astro 让这种错误很难犯;Next.js 让它很容易犯 —— 但同时也给了你避免它的工具。
渲染模型
SEO 始于一个问题:爬虫在初始 HTML 响应里拿到的是什么? Google 确实会执行 JavaScript,但渲染是一个延迟的、有预算限制的第二波 —— 所以只在 hydration 之后才存在的内容,等于是你在押注一个渲染队列会把它呈现出来。(我们在 JavaScript SEO 指南 中深入讲解了这条管线。)
Astro:静态优先 + islands
Astro 的默认行为是静态站点生成。在构建时,它运行你的 .astro 组件,产出完整的 HTML,并且 —— 这是关键 —— 除非你用 client:* 指令显式让某个组件参与 hydration,否则它会剥离框架的 JavaScript。这就是「islands 架构」:页面是静态 HTML,只有交互的那部分会变成被 hydrate 的 island。
---
// src/pages/blog/[slug].astro — runs at build time, never ships to the client
const { slug } = Astro.params;
const post = await getPost(slug);
---
<article>
<h1>{post.title}</h1>
<div set:html={post.html} />
<!-- Only this counter ships JS. The rest is inert HTML. -->
<LikeButton client:visible postId={post.id} />
</article>
爬虫在第一个字节就收到了完整成形的文章。没有需要等待的第二波渲染,因为已经没有任何东西要渲染了。对内容而言,这已是极致。当某个路由确实需要按请求获取数据时,Astro 也能通过适配器(@astrojs/node、Cloudflare、Vercel)做 SSR,所以「静态优先」并不等于「只能静态」。
Next.js:服务端组件与按请求渲染
使用 App Router 的 Next.js 默认采用 React Server Components。页面在服务端渲染(静态路由则在构建时),把 HTML 发到浏览器,外加一份 RSC payload,让 React 得以协调并 hydrate 客户端组件。你能在初始响应里拿到 HTML —— 这对抓取有利 —— 但页面同时也会带上 React 运行时并在客户端重新 hydrate。
Next.js 给了你更丰富的渲染模式菜单,按路由逐个选择:
- 静态(SSG) ——
export const dynamic = 'force-static',或干脆没有动态数据;在构建时预渲染。 - SSR —— 按请求渲染,适用于会随用户、地理位置或时间变化的内容。
- ISR ——
export const revalidate = 3600;先返回静态内容,在后台重新生成。非常适合那些更新频繁、但不需按请求变化的大型内容站。
// app/blog/[slug]/page.tsx — Server Component, runs on the server
export const revalidate = 3600; // ISR: re-generate at most hourly
export async function generateStaticParams() {
const posts = await getAllSlugs();
return posts.map((slug) => ({ slug }));
}
export default async function Page({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
);
}
💡 提示:对 SEO 而言,Next.js 中危险的模式是那种客户端的
'use client'页面,在useEffect里去拉取它的主要内容。这些内容在初始 HTML 里是不可见的,完全依赖 Google 的渲染波。如果一个路由的主要内容能在服务端渲染,那就在服务端渲染它。
实际的结论是:在各自现代的、推荐的配置下,两者默认都会输出可索引的 HTML。 Astro 输出 HTML 时不附带任何客户端框架;Next.js 输出 HTML 时则伴随一个 hydration 步骤。正是这一个差异,驱动了后面的大部分内容。
性能与 Core Web Vitals
Core Web Vitals(LCP、INP、CLS)是一个排名信号,更重要的是,它是真实用户是否体验良好的代理指标。框架的选择,在发送了多少 JavaScript、以及其中有多少必须在页面可交互之前执行这一点上体现得最为明显。
默认负载
一个典型的 Astro 内容页面只会发送其 islands 所需的 JS —— 在一篇纯文章上往往字面意义上为零。一个典型的 Next.js App Router 页面会发送 React 运行时、加上 RSC flight payload、再加上任何客户端组件的代码。这不是「臃肿」 —— 这是 React 应用的成本 —— 但在一个 95% 都是文字的页面上,你是在为用不到的交互能力付费。
| 页面类型 | Astro 典型客户端 JS | Next.js 典型客户端 JS |
|---|---|---|
| 静态文章,无组件 | ~0 KB | ~80–110 KB(运行时 + RSC) |
| 文章 + 一个评论组件 | ~15–30 KB(一个 island) | ~90–130 KB |
| 高度交互的仪表盘 | 重度 hydrate 后与 Next 相当 | ~150 KB+(它的主场) |
具体数字会随你的依赖而变 —— 用 next build 的输出或 Astro 的 bundle 报告测自己的构建 —— 但其形态是一致的:Astro 从接近零起步,只在你要求的地方加 JS;Next 从一个运行时下限起步,并从那里往上增长。
Hydration 成本与 INP
LCP 主要关乎让最大元素尽快绘制出来 —— 两个框架在服务端渲染时都做得不错。更尖锐的分歧在于 INP(Interaction to Next Paint),它会惩罚主线程上的工作。Hydrate 一棵庞大的 React 树会阻塞主线程;在低端移动设备上,这会拖慢首次交互。Astro 的 islands 各自独立、惰性地 hydrate(client:visible、client:idle),所以主线程能保持空闲更久。
⚠️ 注意:再快的框架也救不了一个慢的站点。未优化的首屏大图、阻塞渲染的第三方脚本、以及 web 字体造成的布局偏移,都会在任一框架下毁掉 CWV。在归咎于框架之前,先做一次真实的审计(PageSpeed Insights、Lighthouse,或参考我们的 Core Web Vitals 指南)。
用一个针对已构建页面的快速检查,验证实际发送了什么:
# Count bytes of JS the page actually requests
curl -s https://your-site.com/blog/some-post/ \
| grep -oE 'src="[^"]+\.js"' | wc -l
# Or inspect the initial HTML the crawler sees — is the content there?
curl -s https://your-site.com/blog/some-post/ | grep -c "<h1"
内容 vs 应用
决策通常在这里自然见分晓。问问你的站点是什么。
内容站和营销站,选 Astro 取胜。 博客、文档、更新日志、落地页,以及程序化 SEO 页面(成百上千个模板化 URL)大多是文字和图片,偶尔带点交互。Astro 的 content collections 给你带类型的 frontmatter、Markdown/MDX 以及零 JS 输出。你既得到了类 CMS 配置的撰稿体验,又得到了手写 HTML 的性能。
Astro content collection — typed, build-time, zero client JS
src/content/
blog/ ← MDX + frontmatter schema (Zod)
config.ts ← defineCollection() validates every post at build
交互式应用,选 Next.js 取胜。 如果核心产品是一个应用 —— 需登录的仪表盘、实时数据、复杂表单、有状态的流程 —— 而 SEO 对环绕在它周围的营销或内容页面才重要,那么 Next.js 让你能在一个框架、一次部署里把整件事建起来。你避免了运行两套技术栈(一个 Astro 营销站加一个独立的 React 应用)以及它们之间在路由/鉴权上的接缝。App Router 的 ISR 和 SSR 也能处理那些真正动态、且与 SEO 相关的页面(带实时定价的商品页、地区页),而纯 SSG 模型会逼着你不停地重新构建它们。
灰色地带是带一些动态性的电商和大型内容站。 两者都行。如果页面必须大规模反映按请求的状态(库存、个性化),选 Next.js;如果商品目录大体静态、且你能重新构建或对少数动态路由用按需 SSR,选 Astro。
i18n 与结构化数据
国际化 SEO 的成败系于 hreflang,而富媒体结果依赖 JSON-LD。两个框架都不会完全替你做好这件事 —— 标记由你拥有 —— 但路由支持上有差别。
hreflang
两个框架都期望你发出成对互指的 hreflang link 标签。Astro 有内置的 i18n 路由(locale 前缀、默认 locale 配置),让生成备选 URL 变得直截了当;Next.js 的 Pages Router 曾有一个 i18n 配置,而 App Router 推动你转向基于文件夹的 locale 分段(app/[locale]/...)外加 next-intl 这类库。两种情况下,标签都得你自己生成:
<!-- The same set of tags belongs on every language version of a page -->
<link rel="alternate" hreflang="en" href="https://ex.com/en/blog/post/" />
<link rel="alternate" hreflang="fr" href="https://ex.com/fr/blog/post/" />
<link rel="alternate" hreflang="x-default" href="https://ex.com/en/blog/post/" />
💡 提示:最常见的
hreflang错误是非互指标注 —— 页面 A 指向 B,但 B 没有指回 A。从单一可信源(一份 locale → URL 的映射)生成整个集群,并在每个变体上渲染它。我们的深入讲解收录在国际化 SEO 指南中。
结构化数据(JSON-LD)
JSON-LD 不过是一个 script 标签,所以两个框架在精神上处理方式完全一致 —— 差别只在于你把它放在哪里。在 Astro 里你把它内联进模板;在 Next.js 里你从组件中渲染它,或者在 generateMetadata 中构建它。
---
// Astro: build the object in frontmatter, render as a script tag
const schema = {
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.title,
datePublished: post.pubDate.toISOString(),
};
---
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
// Next.js: render JSON-LD from a Server Component
export default function Page({ post }) {
const schema = {
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.title,
datePublished: post.pubDate,
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
无论用哪个框架,都用 Google 的 Rich Results Test 验证输出 —— 缺少一个必填属性会静默失败,而你永远拿不到富媒体结果。
何时选哪个
按场景给出具体建议:
- 博客、文档或营销站 → Astro。 零 JS 默认、content collections,用最少的纪律就能尽力拿到好的 CWV。
- 大规模程序化 SEO(成千上万个模板化页面)→ 如果页面偏静态,选 Astro;如果需要频繁或按请求的新鲜度,选 带 ISR 的 Next.js。
- 带营销站和应用的 SaaS → 想要一套技术栈就选 Next.js;想要营销页面最高速度、又不介意两次部署,就选 Astro 做营销 + 你自己的应用框架。
- 电商 → 大规模按请求的库存/定价/个性化选 Next.js;大体静态的目录选 Astro。
- SEO 次要的高度交互应用 → Next.js。 这里 SEO 不是问题;按开发体验来选。
- 深谙 React、出活快的团队 → 即便是内容,Next.js 也能降低阻力 —— 前提是你对客户端 JS 保持纪律。
🧑💻 开发者视角:一个流行且完全成立的模式是两者都用 —— 用 Astro 做营销/博客子域名或
/blog路径,用 Next.js(或你的 SPA)做app.*。你在 SEO 重要的地方拿到 Astro 的内容性能,在不重要的地方拿到 React 的交互性。代价是两套构建和一套共享的设计系统。
收尾
别再去找那个让 Google 给好处的框架了 —— 它不存在。只要按现代方式配置,Next.js 和 Astro 都会产出可抓取、可排名的 HTML。真正的决策关乎默认行为与失败模式:Astro 让快速、零 JS 的内容页面成为默认,也成为偷懒时的那条路;Next.js 让统一的、交互式的应用成为默认,并给你各种渲染模式,让你在投入纪律之后能保持动态页面对 SEO 友好。
站点就是内容时,选 Astro。站点是一个同时也带内容的应用时,选 Next.js。拿不准时,在争论之前先在一个真实构建上测一测初始 HTML 和发送出去的 JS。
下一步:
- Astro SEO 指南 —— 端到端地把一个内容站配置好做 SEO。
- JavaScript SEO 与渲染 —— Google 如何渲染 JS,以及如何在 SSG/SSR/ISR 之间选择。