Skip to content

Head 管理

Nuxt 提供多种方式管理页面的 <head> 标签。正确管理 Head 标签是 SEO 优化和社交分享的基础。

useHead vs useSeoMeta 怎么选?

方式适用场景优势
app.head全局默认配置所有页面生效
useHead通用 Head 管理灵活,支持所有标签
useSeoMetaSEO 元标签语法更简洁,类型安全
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 接受 RefComputedRef 或 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 的优先级

  1. app.head 设置全局默认值
  2. 页面级 useHead 可以覆盖或追加
  3. 子组件的 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' },
  ],
})

注意事项

  1. 避免重复 meta:相同 name 的 meta 会被覆盖,不同 name 的会追加
  2. SSR 安全useHead 在 SSR 和客户端都会执行,确保数据在两端都可用
  3. 响应式标题:使用 computed 或 getter 函数实现动态标题
  4. 加载顺序:全局 app.head 先加载,页面级 useHead 可以覆盖
  5. JSON-LD:使用 script 标签 + type: 'application/ld+json' 添加结构化数据

知识脉络

text
样式与资源 → 你在这里:Head 管理

              ├─→ 下一步:SEO Meta

              └─→ 相关:插件与中间件(第三方脚本加载)

基于 Nuxt 4 官方文档整理编写