通过 Layout 统一管理 <head>
创建 SEO 友好的 BaseLayout
在 Astro 中,最佳实践是在 Layout 组件中集中管理 <head> 内容,通过 Props 接收每个页面的个性化元数据。
---
// src/layouts/BaseLayout.astro
interface Props {
title: string;
description: string;
image?: string;
noindex?: boolean;
}
const {
title,
description,
image = '/og-default.png',
noindex = false,
} = Astro.props;
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
const fullTitle = `${title} — 我的网站`;
---
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{fullTitle}</title>
<meta name="description" content={description}>
{noindex && <meta name="robots" content="noindex">}
<!-- Canonical URL -->
<link rel="canonical" href={canonicalUrl}>
<!-- Open Graph -->
<meta property="og:type" content="website">
<meta property="og:title" content={fullTitle}>
<meta property="og:description" content={description}>
<meta property="og:image" content={new URL(image, Astro.site)}>
<meta property="og:url" content={canonicalUrl}>
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content={fullTitle}>
<meta name="twitter:image" content={new URL(image, Astro.site)}>
<slot name="head" /> <!-- 允许页面向 head 注入额外内容 -->
</head>
<body>
<slot />
</body>
</html>
@astrojs/sitemap:自动生成 Sitemap
npx astro add sitemap
// astro.config.mjs
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://www.mysite.com', // 必须设置 site
integrations: [
sitemap({
// 排除特定页面
filter: (page) => !page.includes('/private/'),
// 自定义条目
customPages: ['https://www.mysite.com/page'],
// 更改频率
changefreq: 'weekly',
lastmod: new Date(),
}),
],
});
JSON-LD 结构化数据
---
const post = await getEntry('blog', 'my-post');
const jsonLd = {
"@context": "https://schema.org",
"@type": "Article",
"headline": post.data.title,
"datePublished": post.data.pubDate.toISOString(),
"author": {
"@type": "Person",
"name": post.data.author
},
"image": post.data.image?.url,
"url": new URL(`/blog/${post.slug}`, Astro.site),
};
---
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script>
@astrojs/rss:RSS 源
// src/pages/rss.xml.ts
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
import type { APIContext } from 'astro';
export async function GET(context: APIContext) {
const posts = await getCollection('blog');
return rss({
title: '我的博客',
description: '关于 Web 开发的文章',
site: context.site!,
items: posts.map(post => ({
title: post.data.title,
pubDate: post.data.pubDate,
description: post.data.description,
link: `/blog/${post.slug}/`,
})),
});
}
SEO 检查清单
每个页面确保有:✅ 唯一的 title(55 字符以内)✅ 描述性的 meta description(155 字符以内)✅ Canonical URL 防止重复内容 ✅ Open Graph 标签(分享预览)✅ 图片有 alt 属性 ✅ 语义化 HTML(h1 只有一个)✅ 核心 Web 指标(Lighthouse 90+)
SEO 关键概念详解
Canonical URL(规范链接)
当同一内容有多个 URL(如 /blog/post 和 /blog/post/,或带查询参数的版本),Canonical 链接告诉搜索引擎"这些是同一内容,请以这个 URL 为准"。防止重复内容惩罚(duplicate content penalty)。Astro 中用
new URL(Astro.url.pathname, Astro.site) 自动生成。Open Graph 协议
Facebook 制定的元数据协议,现已成为社交媒体分享预览的通用标准。og:title/description/image/url 决定了链接分享到微信、Twitter、Slack 时显示的卡片内容。og:image 建议尺寸 1200×630 像素。
JSON-LD 结构化数据
Schema.org 定义的机器可读数据格式,帮助搜索引擎理解页面内容语义。Article/BlogPosting 类型可以触发 Google 搜索的富文本摘要(标题、日期、作者显示在搜索结果中),显著提升点击率。
Sitemap.xml
告知搜索引擎爬虫网站的所有 URL 及其优先级、更新频率。对于大型静态站点(数百页),Sitemap 是确保所有页面被索引的重要手段。@astrojs/sitemap 在构建时自动扫描所有路由生成。
Open Graph 图片生成(动态 OG 图)
// src/pages/og/[slug].png.ts
// 为每篇文章动态生成 OG 图片(使用 @vercel/og 或 satori)
import { ImageResponse } from '@vercel/og';
import { getEntry } from 'astro:content';
export async function GET({ params }) {
const post = await getEntry('blog', params.slug);
return new ImageResponse(
// JSX(服务端 HTML)转换为图片
<div style={{ display: 'flex', width: '1200px', height: '630px',
background: '#FF5D01', padding: '60px', color: 'white' }}>
<h1 style={{ fontSize: '60px' }}>{post?.data.title}</h1>
</div>,
{ width: 1200, height: 630 }
);
}
// 在 Layout 中引用:
// <meta property="og:image" content={new URL(`/og/${post.slug}.png`, Astro.site)} />
本章小结
本章核心要点
- Layout 集中管理 head:所有页面通过 BaseLayout 组件统一设置 title/description/og tags,通过 Props 传入个性化内容;slot name="head" 允许特殊页面注入额外的 head 内容。
- Canonical URL 必须设置:防止 /page 和 /page/ 被搜索引擎视为重复内容;用 Astro.site + Astro.url.pathname 自动生成,不要硬编码 URL。
- @astrojs/sitemap 自动化:安装后构建时自动扫描所有静态路由(SSR 页面需要手动添加);必须在 astro.config.mjs 中设置 site 字段,否则无法生成正确的完整 URL。
- JSON-LD 结构化数据:用 set:html={JSON.stringify(jsonLd)} 内联到 script 标签;Article/BreadcrumbList/FAQ 是最常用的类型;可用 Google Rich Results Test 验证。
- 动态 OG 图片:每篇文章生成个性化分享图,大幅提升社交媒体点击率;Astro 的 SSR API 端点 + @vercel/og/satori 是目前主流的实现方案。