配置形式
export default defineNuxtConfig({ app: { head: { charset: 'utf-8', viewport: 'width=device-width, initial-scale=1', meta: [ // .... ], }, },})head 能够配置的项目有:
{ "meta": [ { "name": "viewport", "content": "width=device-width, initial-scale=1" }, { "charset": "utf-8" } ], "link": [], "style": [], "script": [], "noscript": []}例如:
app: { head: { meta: [ // <meta name="viewport" content="width=device-width, initial-scale=1"> { name: 'viewport', content: 'width=device-width, initial-scale=1' } ], script: [ // <script src="https://myawesome-lib.js"></script> { src: 'https://awesome-lib.js' } ], link: [ // <link rel="stylesheet" href="https://myawesome-lib.css"> { rel: 'stylesheet', href: 'https://awesome-lib.css' } ], // please note that this is an area that is likely to change style: [ // <style type="text/css">:root { color: red }</style> { children: ':root { color: red }', type: 'text/css' } ], noscript: [ // <noscript>JavaScript is required</noscript> { children: 'JavaScript is required' } ] }}另外关于这些值的类型:
interface MetaObject { title?: string titleTemplate?: string | ((title?: string) => string) templateParams?: Record<string, string | Record<string, string>> base?: Base link?: Link[] meta?: Meta[] style?: Style[] script?: Script[] noscript?: Noscript[] htmlAttrs?: HtmlAttributes bodyAttrs?: BodyAttributes}这些复合类型是在 @unhead/schema 包中定义的,具体可以查看 unhead schema 的源码
组合式函数
配置的形式往往都是写死的,并且所有页面通用的。无法使用响应式数据,Nuxt 中提供了组合式函数 useHead 来为 meta 信息提供响应式的修改。相比配置文件的形式,useHead 的优势在于:
- 动态修改 <head>:在不同的页面或者组件中可以设置不同的 title、meta 信息
- 支持响应式数据
- 避免全局污染
<script setup lang="ts">useHead({ title: '用户管理系统', meta: [{ name: 'description', content: '这是一个 Nuxt3 网站' }], bodyAttrs: { class: 'test', }, script: [{ innerHTML: "console.log('Hello world')" }],})</script>除了 useHead 以外,还有一个 useHeadSafe:
- 对输入内容进行 HTML 转义,防止恶意脚本注入。
- 适用于不可信内容(如用户输入、外部 API 返回的数据等)。
- 通过转义特殊字符(如
<,>,&),确保内容以纯文本形式显示,而非可执行代码。
例如:
const userInput = '<script>alert("恶意代码")</script>'useHeadSafe({ title: () => userInput,})两者的对比如下:
| 特性 | useHead | useHeadSafe |
|---|---|---|
| 安全性 | 无自动转义,需手动处理不安全内容 | 自动转义,防止 XSS 攻击 |
| 适用场景 | 可信内容(硬编码或已清理的数据) | 不可信内容(用户输入、外部数据源) |
| 性能 | 无额外处理开销 | 轻微开销(转义操作) |
| 动态内容支持 | 支持响应式对象和函数 | 同样支持响应式对象和函数 |
因此在使用时建议优先使用 useHeadSafe,特别是当内容来自用户输入、URL 参数、数据库或第三方 API 时,始终使用 useHeadSafe。在内容完全可信(如静态字符串)时可以使用 useHead,使用时避免直接插入未经验证的动态内容。
除了 head 相关的组合式函数以外,Nuxt 中还提供了一个专门处理 meta 的组合式函数 useSeoHead。虽然 useHead 也能够设置 meta 信息,例如:
useHead({ title: '我的页面标题', // 设置标题 script: [{ src: '/analytics.js' }], // 插入脚本 link: [{ rel: 'icon', href: '/favicon.ico' }] // 插入链接 meta: [ { name: 'description', content: '页面描述' }, // 普通 meta 标签 { property: 'og:title', content: 'Open Graph 标题' }, // Open Graph 标签 { name: 'twitter:card', content: 'summary_large_image' } // Twitter 卡片标签 ]});不过使用 useSeoHead 能够简化常见 SEO 属性的配置,useSeoHead 具备如下特点:
- 语义化语法:通过更直观的键值对(如
ogTitle代替property: 'og:title')配置标签。 - 自动推导:自动将驼峰式键名转换为 SEO 标准的属性名(如
ogTitle→og:title)。 - 类型安全:提供 TypeScript 类型提示,减少拼写错误。
- 内置安全性:默认对动态内容进行转义,防止 XSS 攻击(类似
useHeadSafe的行为)。 - 仅支持 SEO 相关标签:无法操作
<script>、<link>等非 SEO 标签。
useSeoMeta({ title: '我的页面标题', // 自动设置 <title> 和 og:title description: '页面描述', // 自动设置 description 和 og:description ogTitle: 'Open Graph 标题', // 自动转换为 property="og:title" twitterCard: 'summary_large_image', // 自动转换为 name="twitter:card" ogImage: 'https://example.com/image.jpg', // 自动处理为 og:image})两者之间的对比如下表:
| 特性 | useHead | useSeoMeta |
|---|---|---|
| 设计目标 | 通用头部管理 | 专注于 SEO Meta 标签优化 |
| 语法 | 需手动编写 HTML 标签结构 | 语义化键名(驼峰式 → 自动转换) |
| 类型安全 | 基础类型支持 | 强类型提示(SEO 专用键名) |
| 安全性 | 需手动处理动态内容(依赖 useHeadSafe) | 自动转义动态内容 |
| 适用场景 | 任意 <head> 标签操作 | 仅 SEO 相关 meta 标签(title、og、twitter 等) |
| 代码简洁性 | 较低(需完整结构) | 较高(简化配置) |
相关组件
Nuxt 提供了一系列与 <head> 相关的组件,允许开发者直接在 Vue 模板中声明式地操作页面元数据(SEO 相关标签)。这些组件包括:
<Title>:动态设置页面标题(对应<title>标签)。<Meta>:设置<meta>标签(如描述、关键词、Open Graph 等)。<Base>:设置<base>标签(定义页面的基准 URL)。<NoScript>:设置<noscript>标签(脚本未启用时的替代内容)。<Style>:插入<style>标签(内联 CSS)。<Link>:插入<link>标签(如外部样式表、图标)。<Body>:操作<body>标签的属性(如类名、内联样式)。<Html>:操作<html>标签的属性。<Head>:包裹其他头部组件(逻辑容器,不影响实际渲染位置)。
注意事项:
-
书写时需大写:因为这些是组件,需要防止和原生 html 元素冲突
-
实际渲染位置由 Nuxt 自动控制,不会因为嵌套关系改变。这种设计主要是为了代码可读性和逻辑组织,而非影响渲染结果。
-
所有组件属性均支持 Vue 的响应式数据
<template><Title>{{ title }}</Title><Meta name="description" :content="description" /></template><script setup>const title = ref('Hello World')const description = ref('动态描述')</script> -
一些组件支持 children 属性,用于直接插入子内容。例如
<Style>组件<Style type="text/css" children="body { background-color: green; }" /><style type="text/css">body {background-color: green;}</style>
示例:
<template> <div> <!-- Head 组件包裹其他 SEO 组件 --> <Head> <!-- 动态标题 --> <Title>{{ title }}</Title> <!-- 动态 meta 描述 --> <Meta name="description" :content="title" /> <!-- 内联样式 --> <Style type="text/css" children="body { background-color: green; }" /> </Head>
<!-- 页面内容 --> <h1>{{ title }}</h1> </div></template>
<script setup lang="ts">const title = ref('Hello World')</script>渲染效果:
<html> <head> <title>Hello World</title> <meta name="description" content="Hello World" /> <style type="text/css"> body { background-color: green; } </style> </head> <body> <div> <h1>Hello World</h1> </div> </body></html>-EOF-