SEO Meta
useSeoMeta 和 useServerSeoMeta 是专门用于 SEO 元标签的组合式函数,语法比 useHead 更简洁、类型更安全。
useSeoMeta vs useHead
| 方式 | 设置标题 | 设置 OG 标签 | 类型安全 | 语法 |
|---|---|---|---|---|
useHead | title: 'xxx' | meta: [{ property: 'og:title', content: 'xxx' }] | ❌ 属性名是字符串 | 繁琐 |
useSeoMeta | title: 'xxx' | ogTitle: 'xxx' | ✅ 有完整类型提示 | 简洁 |
useSeoMeta 的优势
- 语法简洁:
ogTitle代替{ property: 'og:title', content: '...' } - 类型安全:所有属性名都有 TypeScript 提示
- 去重:自动处理同名标签的覆盖,不会重复
推荐
SEO 相关的标签统一用 useSeoMeta,其他 Head 标签用 useHead。
useSeoMeta
ts
useSeoMeta({
title: '我的页面',
ogTitle: '社交分享标题',
description: '页面描述',
ogDescription: '社交分享描述',
ogImage: 'https://example.com/og.png',
ogUrl: 'https://example.com/page',
twitterCard: 'summary_large_image',
})响应式
ts
const { data: article } = await useFetch('/api/article/1')
useSeoMeta({
title: () => `${article.value?.title} - 我的博客`,
description: () => article.value?.excerpt ?? '',
ogTitle: () => article.value?.title ?? '',
ogDescription: () => article.value?.excerpt ?? '',
ogImage: () => article.value?.coverImage ?? '',
})响应式写法
使用 getter 函数 () => value,当数据变化时 SEO 标签自动更新。
INFO
️ SSR 时的注意事项:SEO 标签在 SSR 时就需要正确设置(搜索引擎读取的是 HTML 源码) 确保在 useFetch 完成后(await 之后)再调用 useSeoMeta
useServerSeoMeta
仅在服务端设置的 SEO meta,不会发送到客户端:
ts
useServerSeoMeta({
title: '我的页面',
description: '页面描述',
ogTitle: '社交分享标题',
ogDescription: '社交分享描述',
ogImage: 'https://example.com/og.png',
})为什么需要 useServerSeoMeta?
- 搜索引擎只能看到 HTML 源码,SEO meta 在服务端设置就够了
- 不发送到客户端可以减小 Payload 大小
- 客户端不需要这些数据(不需要响应式更新)
推荐
纯 SEO 标签用 useServerSeoMeta,需要客户端响应式更新的用 useSeoMeta。
常用 SEO Meta 属性
基础 SEO
| 属性 | 对应标签 | 说明 | 示例 |
|---|---|---|---|
title | <title> | 页面标题 | '我的页面' |
description | meta[name=description] | 页面描述 | '这是关于...' |
robots | meta[name=robots] | 爬虫指令 | 'index, follow' |
canonical | link[rel=canonical] | 规范 URL | 'https://example.com/page' |
Open Graph
| 属性 | 对应标签 | 说明 |
|---|---|---|
ogTitle | meta[property=og:title] | 分享标题 |
ogDescription | meta[property=og:description] | 分享描述 |
ogImage | meta[property=og:image] | 分享图片 |
ogUrl | meta[property=og:url] | 页面 URL |
ogType | meta[property=og:type] | 内容类型 |
ogSiteName | meta[property=og:site_name] | 站点名 |
ogLocale | meta[property=og:locale] | 语言 |
Twitter
| 属性 | 对应标签 | 说明 |
|---|---|---|
twitterCard | meta[name=twitter:card] | 卡片类型 |
twitterTitle | meta[name=twitter:title] | 标题 |
twitterDescription | meta[name=twitter:description] | 描述 |
twitterImage | meta[name=twitter:image] | 图片 |
twitterSite | meta[name=twitter:site] | 站点账号 |
robots 元标签
控制搜索引擎对页面的抓取行为:
ts
useSeoMeta({
robots: 'index, follow', // ✅ 允许索引(默认行为)
// robots: 'noindex, nofollow', // ❌ 禁止索引(如后台页面)
// robots: 'noindex, follow', // 不索引但跟踪链接
// robots: 'index, nofollow', // 索引但不跟踪链接
// robots: 'noimageindex', // 不索引图片
})何时设置 noindex?
- 后台管理页面
- 用户设置页面
- 搜索结果页
- 临时页面
- 重复内容页面
JSON-LD 结构化数据
JSON-LD 是搜索引擎推荐的结构化数据格式,可以让搜索结果显示更丰富的信息(评分、价格、日期等):
ts
useHead({
script: [
{
type: 'application/ld+json',
children: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Article',
name: article.value?.title,
description: article.value?.excerpt,
author: {
'@type': 'Person',
name: 'Author Name',
},
datePublished: article.value?.publishedAt,
dateModified: article.value?.updatedAt,
image: article.value?.coverImage,
}),
},
],
})常用 JSON-LD 类型
ts
// 产品
{
'@type': 'Product',
name: '产品名',
description: '产品描述',
image: '产品图片',
offers: {
'@type': 'Offer',
price: '99.00',
priceCurrency: 'CNY',
availability: 'https://schema.org/InStock',
},
}
// 面包屑导航
{
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: '首页', item: 'https://example.com' },
{ '@type': 'ListItem', position: 2, name: '博客', item: 'https://example.com/blog' },
{ '@type': 'ListItem', position: 3, name: '文章标题' },
],
}
// FAQ
{
'@type': 'FAQPage',
mainEntity: [
{ '@type': 'Question', name: '问题1', acceptedAnswer: { '@type': 'Answer', text: '答案1' } },
],
}完整示例——文章页 SEO
vue
<script setup lang="ts">
const route = useRoute()
const config = useRuntimeConfig()
const { data: article } = await useFetch(`/api/articles/${route.params.id}`)
const canonicalUrl = computed(() =>
`${config.public.siteUrl}/blog/${route.params.id}`
)
// SEO 元标签(仅服务端)
useServerSeoMeta({
title: () => `${article.value?.title} - 我的博客`,
description: () => article.value?.excerpt ?? '',
ogTitle: () => article.value?.title ?? '',
ogDescription: () => article.value?.excerpt ?? '',
ogImage: () => article.value?.coverImage ?? `${config.public.siteUrl}/default-og.png`,
ogUrl: () => canonicalUrl.value,
ogType: 'article',
ogSiteName: '我的博客',
ogLocale: 'zh_CN',
twitterCard: 'summary_large_image',
twitterTitle: () => article.value?.title ?? '',
twitterDescription: () => article.value?.excerpt ?? '',
twitterImage: () => article.value?.coverImage ?? '',
})
// canonical URL
useHead({
link: [
{ rel: 'canonical', href: () => canonicalUrl.value },
],
})
// JSON-LD 结构化数据
useHead({
script: [
{
type: 'application/ld+json',
children: () => JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Article',
name: article.value?.title,
description: article.value?.excerpt,
author: { '@type': 'Person', name: article.value?.author },
datePublished: article.value?.publishedAt,
image: article.value?.coverImage,
}),
},
],
})
</script>SEO 最佳实践
- 每个页面都设置
title和description:这是最基本的 SEO 要求 - 使用
useServerSeoMeta:SEO 标签不需要客户端,减小 Payload - 设置
canonicalURL:避免重复内容问题 - 设置 Open Graph 标签:确保社交分享时显示正确信息
- 动态页面设置
og:url:避免同一内容被不同 URL 索引 - 后台页面设置
noindex:不需要搜索引擎索引的页面 - 添加 JSON-LD:让搜索结果显示更丰富的信息
知识脉络
text
Head 管理 → 你在这里:SEO Meta
│
├─→ 下一步:Open Graph 与 Twitter
│
└─→ 相关:服务端开发 → 服务路由(sitemap / robots)