Chapter 09

SEO 优化:元数据与 Sitemap

掌握 Astro 中的 SEO 最佳实践,包括元数据管理、自动 Sitemap、RSS 源与结构化数据

通过 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)} />

本章小结

本章核心要点