Skip to content
Nuxt.js 核心组件与 API
Nuxt 在 Vue Router 和 Vue 的基础上封装了几个关键组件与组合式函数,覆盖导航、布局、页面过渡、头部管理和服务端数据读取。它们共同形成了一套页面构建骨架,避免重复编写导航逻辑、布局切换和 SEO 处理。下面逐一说明这些组件的参数、行为以及它们之间的协作方式。
NuxtLink 导航组件
<NuxtLink> 是 Nuxt 对内部/外部链接的统一封装,可以看作 Vue Router <RouterLink> 的增强版。它能够自动判断目标地址是应用内的路由还是外部 URL,内部路由使用客户端导航并启用预加载,外部链接则退化为普通 <a> 标签。
与 RouterLink 的关系
NuxtLink 继承了 RouterLink 的所有 props,并在此基础上补充了预加载、外链处理和一些默认行为。Vue Router 官方文档提到,在中等规模以上应用中建议创建自定义 RouterLink 包装组件来统一外链、激活样式等问题。NuxtLink 直接把这些包装逻辑内置到了框架层。
核心属性
to
接受任何 URL 或 Vue Router 的路由位置对象。传入对象时会自动处理 query 参数的编码,无需手动调用 encodeURI 或 encodeURIComponent。
vue
<!-- 字符串路径 -->
<NuxtLink to="/about">关于</NuxtLink>
<!-- 命名路由 + params -->
<NuxtLink :to="{ name: 'posts-id', params: { id: 123 } }">
文章 #123
</NuxtLink>上面第二个例子最终渲染成 <a href="/posts/123">文章 #123</a>。
replace
与 Vue Router 的 replace 一致,导航时替换当前历史记录条目而不是新增一条。
external
显式声明为外部链接,渲染为 <a> 并绕过 Vue Router 的内部路由匹配。处理 /public 目录下的静态文件时这个属性是必需的——Nuxt 把 /public 中的文件直接作为静态资源提供,如果使用内部链接访问,Vue Router 会尝试匹配路由,找不到对应页面时返回 404:
vue
<NuxtLink to="/example-report.pdf" external>
下载报告
</NuxtLink>prefetch
控制预加载行为。NuxtLink 默认对内部链接进行 prefetch,可以通过这个 prop 关闭或调整。可选值:true、false 或 'interaction'——后者仅在用户 hover 或 focus 时才预加载目标页面的资源,平衡了首屏加载和导航速度。
custom 与 v-slot
当需要完全控制链接的渲染方式时(例如用按钮或图标作为导航元素),设置 custom 并使用默认插槽。NuxtLink 的 v-slot 暴露了 href、navigate、prefetch、shouldPrefetch 等属性,行为与 Vue Router 一致:
vue
<NuxtLink to="/about" custom v-slot="{ href, navigate, prefetch, shouldPrefetch }">
<a
:href="href"
@click="navigate"
@pointerenter="shouldPrefetch('interaction') && prefetch()"
@focus="shouldPrefetch('interaction') && prefetch()"
>
关于页
</a>
</NuxtLink>shouldPrefetch('interaction') 用来判断预加载策略是否设置为交互触发——如果是 false 或默认为 true 时行为会有所不同。
rel 与 noRel
外部链接默认加上 rel="noopener noreferrer"。可以通过 rel 属性覆盖这个值,或者用 noRel 完全移除自动添加的 rel。
预加载的实际作用
Nuxt 的预加载不是简单的 <link rel="prefetch"> 注入,而是在内部协调了资源加载时机。对于使用 prefetch="interaction" 的链接,只有当用户表现出导航意图(指针停留或聚焦)时,才会开始提前获取目标页面的 JavaScript 和异步数据。在慢速网络或应用体积较大的场景下,这个策略对首屏性能的贡献比较直接。
更深入的行为可以追踪 NuxtLink 源码,入口在 packages/nuxt/src/app/components/nuxt-link.ts,其中包含了内部/外部链接判断、预加载触发和默认属性处理逻辑。
NuxtLayout 与布局的动态化
<NuxtLayout> 是 Nuxt 的布局容器组件,页面内容通过布局文件中的 <slot /> 指定渲染位置。
slot 渲染机制
页面组件会作为 default slot 传递给布局组件。layouts/default.vue 中的 <slot /> 会被替换为当前路由对应的页面组件。如果需要特定命名布局,通过 name prop 指定:
vue
<template>
<NuxtLayout name="custom">
<!-- 页面内容自动进入 custom 布局的 slot -->
</NuxtLayout>
</template>页面一侧可以通过 definePageMeta 声明 layout: 'custom' 来对应。
嵌套布局
在布局模板中再使用 <NuxtLayout> 可以实现嵌套布局。但这会引入多级 slot 传递,需要小心控制布局实例的存活范围——内层布局的组件实例在路由切换时不一定重建,取决于 pageKey 和 layoutTransition 的配置。
布局刷新行为
当路由变化时,如果前后的页面使用了同一个布局,布局组件实例不会重新挂载。onMounted 只在第一次进入该布局时触发一次,后续路由变化只会更新 <slot /> 里的内容,不会重新走布局的完整生命周期。
如果需要根据路由变化做某些操作(比如更新导航高亮),可以在布局中 watch route 对象。
这个行为对性能有利——避免了不必要的组件重建,但也意味着布局状态会持续保留。如果确实希望每次导航都重建布局,可以在 definePageMeta 中设置 layoutTransition 或调整 pageKey。
NuxtPage 的页面控制与过渡
<NuxtPage> 是页面组件在应用中的挂载点,通常放在 app.vue 或布局文件中。它支持三个关键属性来管理页面实例的缓存和切换动画。
pageKey
pageKey 决定页面组件实例是复用还是重建,本质上就是 Vue 的 key。默认值是 $route.fullPath,所以每次切换路由都会完全销毁旧页面、创建新页面。如果需要保持某些路由下的页面状态(比如表单输入),可以传入固定字符串或自定义函数:
vue
<NuxtPage :page-key="route => route.params.id || 'default'" />这样相同 id 参数的路由会复用同一个页面组件实例。在页面组件中使用 definePageMeta 的 key 字段可以达到同样效果。
transition
transition 接收一个对象或字符串,内部直接传给 Vue 的 <Transition> 组件。可以定义进入、离开动画的名称和配置:
vue
<NuxtPage :transition="{ name: 'fade', mode: 'out-in' }" />页面组件本身也可以通过 definePageMeta 的 pageTransition 字段单独声明过渡效果,这时会覆盖 <NuxtPage> 上的全局 transition 设置。
keepalive
keepalive 接受 boolean 或 KeepAlive 组件的 props 对象,用于缓存页面组件实例,避免重复创建和销毁。启用 keepalive 时,页面会触发 onActivated 和 onDeactivated 生命周期钩子:
vue
<NuxtPage :keepalive="{ include: ['search'] }" />这里 include 对应页面组件的 name 选项(或 defineOptions 中的 name),只缓存搜索页面,其他页面照常卸载。
definePageMeta 也支持 keepalive 字段,优先级高于 <NuxtPage> 上的设置。
definePageMeta 路由元编程
definePageMeta 是一个编译宏,用在 pages/ 或 layouts/ 目录的组件中,在编译阶段注入路由元信息。它不会在浏览器中实际执行,而是被 Nuxt 构建工具在打包时处理。
常用字段
layout:指定使用的布局名称。middleware:指定一个或多个中间件名称或内联函数。中间件在页面渲染前执行,可做权限验证或重定向。key:与pageKey作用相同,控制组件复用。validate:校验路由有效性,接收当前route对象,返回true或false(或Promise)。返回false时渲染 404 错误页。redirect:设置重定向目标,可以是字符串路径或返回路径的函数。pageTransition/layoutTransition:页面/布局的过渡配置。keepalive:是否启用页面缓存。
validate 与 redirect 的使用
假设某个文章页面依赖 params.id,需要确保 id 合法:
vue
<script setup>
definePageMeta({
validate: (route) => {
const id = parseInt(route.params.id)
return !isNaN(id) && id > 0
}
})
</script>如果验证失败,Nuxt 会跳过该页面并显示错误页。这是比写组件内守卫更早介入的校验手段。
redirect 则直接改变导航目标,常用于路由别名或条件跳转:
vue
<script setup>
definePageMeta({
redirect: (route) => {
if (route.query.redirect)
return route.query.redirect
return '/default'
}
})
</script>所有字段的具体行为可以在 Nuxt 官方文档的 definePageMeta 页面找到。
useHead 组件级头部管理
useHead 是一个组件内可用的组合式函数,用来设置 <head> 中的标题、meta、link、script 等元素。它接受一个配置对象(可以是响应式的),在服务端渲染时生成的标签会包含在 HTML 里,客户端 hydration 之后继续响应式更新。
响应式 meta
最常见的用法是传入 computed 或 ref,让页面标题跟随组件数据变化:
vue
<script setup>
const productName = ref('加载中...')
const { data } = await useAsyncData(() => fetchProduct())
productName.value = data.value.name
useHead({
title: computed(() => `${productName.value} - 商城`),
meta: [
{ name: 'description', content: '产品详情页' }
]
})
</script>服务端在渲染这个页面时,会使用当前 productName 的值生成 <title> 标签。客户端获取到异步数据后,标题会自动更新。
覆盖策略
meta 数组中的每个对象可以设置 key 字段。Nuxt 内部会按照调用顺序合并,后调用的 useHead 中相同 key 的 meta 会覆盖前面的。例如布局中设置了一个通用的 description:
vue
<!-- layouts/default.vue -->
<script setup>
useHead({
meta: [
{ key: 'description', name: 'description', content: '默认站点描述' }
]
})
</script>页面组件再次调用 useHead 并指定相同的 key 时,就会覆盖这个值,实现逐级定制。
SSR 安全
服务端渲染阶段 useHead 生成的标签会随着 HTML 响应一并返回,搜索引擎和社交媒体的爬虫可以直接解析这些信息,不需要等待客户端 JavaScript 执行。客户端接管页面后,useHead 仍然是响应式的,对 SEO 无影响但有助于后续交互中的头部更新。
服务端兼容的组合式函数
Nuxt 中有一部分组合式函数专门处理只能在服务端或同构场景中使用的 API,它们使服务端与客户端之间的数据传递变得直接。
useRequestHeaders
useRequestHeaders 用于在服务端渲染时读取当前入站请求的 HTTP 头部。它在客户端执行时返回空对象,因此不会造成跨环境错误。典型场景是将客户端的 cookie 或认证 token 原样转发给内部 API:
vue
<script setup>
const headers = useRequestHeaders(['cookie'])
const { data } = await useAsyncData(() => {
return $fetch('/api/user', {
headers: {
cookie: headers.cookie
}
})
})
</script>useRequestHeaders(['cookie']) 只在服务端返回实际的 Cookie 字符串,客户端返回空对象,但 useAsyncData 的设计保证了数据在服务端获取后序列化到客户端,所以页面渲染不会缺失数据。
useCookie
useCookie 返回一个响应式 ref,用于安全地读写 cookie。服务端能够直接写入 Set-Cookie 头,客户端通过 document.cookie 操作。选项涵盖了 cookie 的常用属性:maxAge、expires、httpOnly、sameSite、secure、domain、path 等。
vue
<script setup>
const token = useCookie('auth_token', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax'
})
// 登录后写入
token.value = 'new_token_value'
</script>设置为 httpOnly: true 的 cookie 无法被客户端 JavaScript 读取或修改,适合存放服务端鉴权信息。这种情况下 useCookie 主要用作服务端写入,客户端读取到的值为 undefined。
自动导入的原理与边界
Nuxt 会自动导入 composables/、utils/、components/ 目录中的导出,以及 Vue 和 Nuxt 的内置 API(ref、computed、useRouter、useHead 等)。这个过程的实质是在构建阶段扫描文件系统,生成全局类型声明并注入 import 语句。
冲突解决
如果手动 import 了一个名称与自动导入重名的变量,手动导入的优先级更高。这一规则可以解决命名冲突,也能用来明确代码的来源(比如显式地从 vue 引入 ref 以提高可读性)。
显式导入的场景
- 名称冲突:多个包导出了同名变量(例如
defineOptions可能在多个框架中存在),手动指定来源可以消除歧义。 - IDE 支持:虽然 Nuxt 会生成
.d.ts类型声明文件,但仍然有一些编辑器无法完美解析自动导入的来源,手动 import 可以改善跳转和提示。 - 可读性:自动导入的函数来源并不直观,显式 import 能快速指出依赖的上游。
组件与 API 协作实战
下面用一个完整示例说明 layout、page、head、NuxtLink 和 NuxtPage 之间的协作关系。
layouts/default.vue
vue
<template>
<div>
<header>
<nav>
<NuxtLink to="/">首页</NuxtLink>
<NuxtLink to="/about">关于</NuxtLink>
</nav>
</header>
<main>
<slot />
</main>
</div>
</template>pages/index.vue
vue
<script setup>
definePageMeta({
layout: 'default',
middleware: ['auth'],
key: 'home',
pageTransition: {
name: 'fade',
mode: 'out-in'
},
keepalive: true
})
useHead({
title: '首页',
meta: [
{ key: 'description', name: 'description', content: '网站首页' }
]
})
</script>
<template>
<h1>欢迎</h1>
</template>pages/about.vue
vue
<script setup>
definePageMeta({
layout: 'default',
validate: (route) => {
return route.query.preview !== 'false'
}
})
useHead({
title: '关于我们',
meta: [
{ key: 'description', name: 'description', content: '关于页面' }
]
})
</script>
<template>
<h1>关于我们</h1>
<NuxtLink :to="{ name: 'posts-id', params: { id: 1 } }">
查看文章
</NuxtLink>
</template>app.vue
vue
<template>
<NuxtPage :keepalive="false" :transition="{ name: 'page' }" />
</template>在这个结构中:
- 布局
default.vue通过<slot />承载页面内容,并提供导航。 - 每个页面用
definePageMeta声明了layout、key、pageTransition和keepalive,并在about.vue中加入了validate来限制访问条件。 useHead在页面中独立设置标题和 meta,覆盖布局可能存在的默认值。NuxtLink处理内部导航,路由切换由NuxtPage配合页面自身声明的过渡动画渲染。- 自动导入省去了大部分 import 语句,但必要时随时可以显式引入以消除歧义。
这一套协同模式把页面结构、导航、头部管理和路由守卫拆解到了不同的层级,每个层级负责自己的职责。
参考链接
- [1] https://nuxt.com/docs/4.x/api/components/nuxt-link
- [7] https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-link.ts
- [8] https://nuxt.com/docs/4.x/api/components/nuxt-layout
- [9] https://nuxt.com/docs/4.x/api/components/nuxt-page
- [10] https://nuxt.com/docs/4.x/api/utils/define-page-meta
- [11] https://nuxt.com/docs/4.x/api/composables/use-head
- [12] https://nuxt.com/docs/4.x/api/composables/use-request-headers
- [13] https://nuxt.com/docs/4.x/api/composables/use-cookie
- [14] https://nuxt.com/docs/4.x/guide/concepts/auto-imports
- [15] https://router.vuejs.org/guide/advanced/extending-router-link
- [17] https://nuxt.com/docs/4.x/guide/directory-structure/layouts
