Astro 的 SEO 优化功能非常强大,帮助开发者构建搜索引擎友好的网站。了解如何利用 Astro 的 SEO 特性对于提高网站可见性至关重要。
核心 SEO 优势:
- 静态 HTML 输出:默认输出纯 HTML,易于搜索引擎爬取
- 快速加载速度:零 JavaScript 默认,提升 Core Web Vitals
- 服务器端渲染:支持 SSR,确保动态内容也能被索引
- 语义化 HTML:鼓励使用正确的 HTML 标签
Meta 标签配置:
astro--- // src/pages/index.astro const title = "我的网站标题"; const description = "这是网站描述"; const image = "/og-image.jpg"; const url = new URL(Astro.url.pathname, Astro.site); --- <html lang="zh-CN"> <head> <!-- 基本 Meta 标签 --> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <meta name="description" content={description} /> <meta name="keywords" content="astro, seo, web development" /> <!-- Open Graph 标签 --> <meta property="og:type" content="website" /> <meta property="og:title" content={title} /> <meta property="og:description" content={description} /> <meta property="og:image" content={new URL(image, Astro.site)} /> <meta property="og:url" content={url} /> <!-- Twitter Card 标签 --> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content={title} /> <meta name="twitter:description" content={description} /> <meta name="twitter:image" content={new URL(image, Astro.site)} /> <!-- 规范链接 --> <link rel="canonical" href={url} /> <!-- Favicon --> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <!-- 结构化数据 --> <script type="application/ld+json" set:html={JSON.stringify({ "@context": "https://schema.org", "@type": "WebSite", "name": title, "url": url.toString(), "description": description })} /> </head> <body> <slot /> </body> </html>
动态 SEO 组件:
astro--- // src/components/SEO.astro interface Props { title: string; description: string; image?: string; type?: 'website' | 'article'; publishedTime?: Date; modifiedTime?: Date; author?: string; } const { title, description, image = '/og-default.jpg', type = 'website', publishedTime, modifiedTime, author } = Astro.props; const url = new URL(Astro.url.pathname, Astro.site); const imageUrl = new URL(image, Astro.site); --- <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <meta name="description" content={description} /> <meta name="robots" content="index, follow" /> <!-- Open Graph --> <meta property="og:type" content={type} /> <meta property="og:title" content={title} /> <meta property="og:description" content={description} /> <meta property="og:image" content={imageUrl} /> <meta property="og:url" content={url} /> {publishedTime && <meta property="article:published_time" content={publishedTime.toISOString()} />} {modifiedTime && <meta property="article:modified_time" content={modifiedTime.toISOString()} />} {author && <meta property="article:author" content={author} />} <!-- Twitter Card --> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content={title} /> <meta name="twitter:description" content={description} /> <meta name="twitter:image" content={imageUrl} /> <!-- Canonical --> <link rel="canonical" href={url} /> <!-- JSON-LD --> <script type="application/ld+json" set:html={JSON.stringify({ "@context": "https://schema.org", "@type": type === 'article' ? 'Article' : 'WebSite', "headline": title, "description": description, "image": imageUrl.toString(), "url": url.toString(), "datePublished": publishedTime?.toISOString(), "dateModified": modifiedTime?.toISOString(), "author": { "@type": "Person", "name": author } })} />
使用 SEO 组件:
astro--- // src/pages/blog/[slug].astro import SEO from '../../components/SEO.astro'; import { getEntry } from 'astro:content'; const post = await getEntry('blog', Astro.params.slug); const { Content } = await post.render(); --- <SEO title={post.data.title} description={post.data.description} image={post.data.image} type="article" publishedTime={post.data.publishDate} modifiedTime={post.data.updatedDate} author={post.data.author} /> <article> <h1>{post.data.title}</h1> <Content /> </article>
站点地图生成:
typescript// src/pages/sitemap.xml.ts import { getCollection } from 'astro:content'; export async function GET(context) { const posts = await getCollection('blog'); const site = context.site?.toString() || ''; const body = `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> ${posts.map(post => ` <url> <loc>${site}blog/${post.slug}</loc> <lastmod>${post.data.updatedDate || post.data.publishDate}</lastmod> <changefreq>weekly</changefreq> <priority>0.8</priority> </url>`).join('')} <url> <loc>${site}</loc> <lastmod>${new Date().toISOString()}</lastmod> <changefreq>daily</changefreq> <priority>1.0</priority> </url> </urlset>`; return new Response(body, { headers: { 'Content-Type': 'application/xml', 'Cache-Control': 'public, max-age=86400', }, }); }
Robots.txt 配置:
typescript// src/pages/robots.txt.ts export async function GET(context) { const site = context.site?.toString() || ''; const body = `User-agent: * Allow: / Disallow: /api/ Disallow: /admin/ Sitemap: ${site}sitemap.xml`; return new Response(body, { headers: { 'Content-Type': 'text/plain', 'Cache-Control': 'public, max-age=86400', }, }); }
结构化数据:
astro--- // src/components/ArticleSchema.astro interface Props { title: string; description: string; image: string; publishDate: Date; author: string; url: string; } const { title, description, image, publishDate, author, url } = Astro.props; --- <script type="application/ld+json" set:html={JSON.stringify({ "@context": "https://schema.org", "@type": "Article", "headline": title, "description": description, "image": image, "datePublished": publishDate.toISOString(), "dateModified": publishDate.toISOString(), "author": { "@type": "Person", "name": author }, "publisher": { "@type": "Organization", "name": "My Website", "logo": { "@type": "ImageObject", "url": "/logo.png" } }, "mainEntityOfPage": { "@type": "WebPage", "@id": url } })} />
面包屑导航:
astro--- // src/components/Breadcrumb.astro interface Props { items: Array<{ name: string; href: string; }>; } const { items } = Astro.props; --- <nav aria-label="Breadcrumb"> <ol class="breadcrumb"> {items.map((item, index) => ( <li class="breadcrumb-item"> {index === items.length - 1 ? ( <span aria-current="page">{item.name}</span> ) : ( <a href={item.href}>{item.name}</a> )} </li> ))} </ol> </nav> <script type="application/ld+json" set:html={JSON.stringify({ "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": items.map((item, index) => ({ "@type": "ListItem", "position": index + 1, "name": item.name, "item": new URL(item.href, Astro.site).toString() })) })} /> <style> .breadcrumb { display: flex; list-style: none; padding: 0; margin: 1rem 0; } .breadcrumb-item:not(:last-child)::after { content: ' / '; margin: 0 0.5rem; } .breadcrumb-item a { color: #0066cc; text-decoration: none; } .breadcrumb-item a:hover { text-decoration: underline; } </style>
性能与 SEO:
astro--- // src/pages/index.astro import { Image } from 'astro:assets'; import heroImage from '../assets/hero.jpg'; --- <!-- 优化图片加载 --> <Image src={heroImage} alt="Hero Image" width={1200} height={630} format="webp" loading="eager" priority={true} /> <!-- 预加载关键资源 --> <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin /> <link rel="preconnect" href="https://api.example.com" /> <!-- 内联关键 CSS --> <style> /* 关键 CSS */ .hero { min-height: 60vh; } </style>
最佳实践:
-
Meta 标签:
- 为每个页面设置唯一的 title 和 description
- 使用 Open Graph 和 Twitter Card
- 设置规范链接避免重复内容
-
结构化数据:
- 使用 JSON-LD 格式
- 实现正确的 Schema 类型
- 使用 Google 结构化数据测试工具验证
-
性能优化:
- 优化 Core Web Vitals
- 使用图片优化
- 实现代码分割
-
内容优化:
- 使用语义化 HTML
- 优化标题层级(H1-H6)
- 提供有意义的 alt 文本
-
技术 SEO:
- 生成站点地图
- 配置 robots.txt
- 实现面包屑导航
Astro 的 SEO 功能帮助开发者构建搜索引擎友好的网站,提高在线可见性。