Skip to content

meta相关处理

配置形式

nuxt.config.ts
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,
})

两者的对比如下:

特性useHeaduseHeadSafe
安全性无自动转义,需手动处理不安全内容自动转义,防止 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 标准的属性名(如 ogTitleog: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
})

两者之间的对比如下表:

特性useHeaduseSeoMeta
设计目标通用头部管理专注于 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>:包裹其他头部组件(逻辑容器,不影响实际渲染位置)。

注意事项:

  1. 书写时需大写:因为这些是组件,需要防止和原生 html 元素冲突

  2. 实际渲染位置由 Nuxt 自动控制,不会因为嵌套关系改变。这种设计主要是为了代码可读性和逻辑组织,而非影响渲染结果。

  3. 所有组件属性均支持 Vue 的响应式数据

    <template>
    <Title>{{ title }}</Title>
    <Meta name="description" :content="description" />
    </template>
    <script setup>
    const title = ref('Hello World')
    const description = ref('动态描述')
    </script>
  4. 一些组件支持 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-