Head 管理
Nuxt 提供多种方式管理页面的 <head> 标签。正确管理 Head 标签是 SEO 优化和社交分享的基础。
useHead vs useSeoMeta 怎么选?
| 方式 | 适用场景 | 优势 |
|---|---|---|
app.head | 全局默认配置 | 所有页面生效 |
useHead | 通用 Head 管理 | 灵活,支持所有标签 |
useSeoMeta | SEO 元标签 | 语法更简洁,类型安全 |
useServerSeoMeta | 仅服务端 SEO | 不发送到客户端,减小 Payload |
简单记忆
- 设置
<meta>、<link>、<script>等 →useHead - 设置 SEO 元标签(title、description、OG) →
useSeoMeta - 全局默认值 →
app.head
useHead
在组件中动态设置 head 属性,支持响应式:
基本用法
ts
useHead({
title: '我的页面',
meta: [
{ name: 'description', content: '页面描述' },
{ name: 'keywords', content: 'nuxt, vue, ssr' },
],
link: [
{ rel: 'icon', href: '/favicon.ico' },
{ rel: 'stylesheet', href: '/css/custom.css' },
],
script: [
{ src: '/js/analytics.js', defer: true },
],
style: [
{ children: 'body { margin: 0; }' },
],
})响应式 Head
ts
const title = computed(() => `${article.value?.title} - 我的博客`)
useHead({
title,
meta: [
{ name: 'description', content: () => article.value?.excerpt ?? '' },
],
})响应式 Head 的原理
useHead 接受 Ref、ComputedRef 或 getter 函数。当响应式值变化时,Head 标签会自动更新。
推荐用 computed 或 getter 函数
写法更简洁:
ts
// ✅ 推荐:getter 函数
useHead({
title: () => article.value?.title ?? '',
})
// ✅ 也行:computed
const title = computed(() => article.value?.title ?? '')
useHead({ title })useHead 的完整选项
ts
useHead({
// <title> 标签
title: '页面标题',
// <html> 标签属性
htmlAttrs: { lang: 'zh-CN', dir: 'ltr' },
// <body> 标签属性
bodyAttrs: { class: 'dark-mode' },
// <meta> 标签
meta: [
{ name: 'description', content: '描述' },
{ property: 'og:title', content: '社交分享标题' },
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
// <link> 标签
link: [
{ rel: 'canonical', href: 'https://example.com/page' },
{ rel: 'icon', href: '/favicon.ico' },
{ rel: 'alternate', hreflang: 'en', href: 'https://example.com/en' },
{ rel: 'preload', href: '/fonts/inter.woff2', as: 'font', type: 'font/woff2', crossorigin: '' },
],
// <script> 标签
script: [
{ src: '/js/analytics.js', defer: true },
{ type: 'application/ld+json', children: JSON.stringify({ /* JSON-LD */ }) },
],
// <style> 标签
style: [
{ children: 'body { margin: 0; }' },
],
// <noscript> 标签
noscript: [
{ children: '<p>请启用 JavaScript</p>' },
],
})useHeadSafe
安全版本的 useHead,过滤不安全的标签和属性,防止 XSS 攻击:
ts
useHeadSafe({
script: [
// ✅ 安全:外部脚本
{ src: '/js/trusted.js' },
// ❌ 危险:会被过滤掉(防止 XSS)
{ innerHTML: 'alert("xss")' },
],
})何时用 useHeadSafe?
当 Head 数据来自用户输入或第三方来源时,用 useHeadSafe 防止注入攻击。如果数据是你自己控制的(如文章标题),用 useHead 即可。
app.head 配置
在 nuxt.config.ts 中设置全局默认 head:
ts
export default defineNuxtConfig({
app: {
head: {
title: '我的网站',
titleTemplate: '%s - 我的网站',
meta: [
{ name: 'description', content: '网站描述' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ charset: 'utf-8' },
],
link: [
{ rel: 'icon', href: '/favicon.ico' },
],
htmlAttrs: {
lang: 'zh-CN',
},
},
},
})app.head vs useHead 的优先级
app.head设置全局默认值- 页面级
useHead可以覆盖或追加 - 子组件的
useHead可以覆盖父组件的设置
titleTemplate
为页面标题添加统一后缀,避免每个页面手动写后缀:
ts
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
title: '我的网站', // 默认标题(页面没设置时使用)
titleTemplate: '%s - 我的网站', // 页面设置 title 时自动加后缀
},
},
})ts
// 页面中
useHead({ title: '首页' })
// 最终标题:首页 - 我的网站
useHead({ title: '关于我们' })
// 最终标题:关于我们 - 我的网站函数形式的 titleTemplate(更灵活):
ts
// nuxt.config.ts
app: {
head: {
titleTemplate: (title) => {
// 没有 title 时返回默认标题
return title ? `${title} | 我的网站` : '我的网站'
},
},
}titleTemplate 的替换逻辑
useHead({ title: '首页' })→%s被替换为首页→首页 - 我的网站- 如果页面没设置
title,则使用app.head.title作为默认值
推荐
所有项目都设置 titleTemplate,确保标题格式一致。
页面级 vs 全局
| 方式 | 作用范围 | 响应式 | 适用场景 |
|---|---|---|---|
app.head | 全局 | ❌ | 默认 title、charset、viewport |
useHead | 当前页面 | ✅ | 动态标签、脚本加载 |
useSeoMeta | 当前页面 | ✅ | SEO 元标签 |
useServerSeoMeta | 当前页面(仅服务端) | ✅ | 不需要客户端的 SEO 标签 |
常见场景
加载第三方脚本
ts
// Google Analytics
useHead({
script: [
{
src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX',
async: true,
},
{
children: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
`,
},
],
})添加 canonical URL
ts
const route = useRoute()
const config = useRuntimeConfig()
useHead({
link: [
{
rel: 'canonical',
href: () => `${config.public.siteUrl}${route.path}`,
},
],
})canonical URL 的作用
告诉搜索引擎这是页面的规范 URL,避免同一内容因不同 URL 被重复索引。
国际化多语言标签
ts
useHead({
link: [
{ rel: 'alternate', hreflang: 'zh', href: 'https://example.com/zh/page' },
{ rel: 'alternate', hreflang: 'en', href: 'https://example.com/en/page' },
{ rel: 'alternate', hreflang: 'x-default', href: 'https://example.com/page' },
],
})注意事项
- 避免重复 meta:相同
name的 meta 会被覆盖,不同name的会追加 - SSR 安全:
useHead在 SSR 和客户端都会执行,确保数据在两端都可用 - 响应式标题:使用
computed或 getter 函数实现动态标题 - 加载顺序:全局
app.head先加载,页面级useHead可以覆盖 - JSON-LD:使用
script标签 +type: 'application/ld+json'添加结构化数据
知识脉络
text
样式与资源 → 你在这里:Head 管理
│
├─→ 下一步:SEO Meta
│
└─→ 相关:插件与中间件(第三方脚本加载)