Lzh on GitHub

pages

Nuxt 提供基于文件的路由,用于在您的 Web 应用程序中创建路由。
为了减小应用程序的 bundle 大小,此目录是 可选的,这意味着如果您只使用 app.vue,则不会包含 vue-router。要强制使用 pages 系统,请在 nuxt.config 中设置 pages: true,或者拥有一个 app/router.options.ts

用法

Pages 是 Vue 组件,可以具有 Nuxt 支持的任何 有效扩展名(默认情况下为 .vue, .js, .jsx, .mjs, .ts.tsx)。

Nuxt 将自动为您的 ~/pages/ 目录中的每个页面创建一个路由。

<template>
  <h1>Index page</h1>
</template>

pages/index.vue 文件将映射到您应用程序的 / 路由。

如果您正在使用 app.vue,请确保使用 <NuxtPage/> 组件来显示当前页面:

app.vue
<template>
  <div>
    <!-- Markup shared across all pages, ex: NavBar -->
    <NuxtPage />
  </div>
</template>

Pages 必须具有单个根元素,以允许页面之间的 路由过渡。HTML 注释也被认为是元素。

这意味着当路由进行服务器渲染或静态生成时,您将能够正确地看到其内容,但是当您在客户端导航期间导航到该路由时,路由之间的过渡将失败,并且您会看到该路由不会被渲染。

以下是一些示例,说明了具有单个根元素的页面的外观:

<template>
  <div>
    <!-- This page correctly has only one single root element -->
    Page content
  </div>
</template>

动态路由

如果您在方括号内放置任何内容,它将转换为 动态路由参数。您可以在文件名或目录中混合和匹配多个参数,甚至是非动态文本。

如果您希望参数是 可选的,则必须将其放在双重方括号中 - 例如,~/pages/[[slug]]/index.vue~/pages/[[slug]].vue 将匹配 //test

Directory Structure
-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue

给定上面的示例,您可以通过 $route 对象在组件中访问 group/id:

pages/users-[group]/[id].vue
<template>
  <p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>

导航到 /users-admins/123 将渲染:

<p>admins - 123</p>

如果您想使用 Composition API 访问路由,则有一个全局的 useRoute 函数,它允许您像 Options API 中的 this.$route 一样访问路由。

<script setup lang="ts">
const route = useRoute()

if (route.params.group === 'admins' && !route.params.id) {
  console.log('Warning! Make sure user is authenticated!')
}
</script>
命名的父路由将优先于嵌套的动态路由。对于 /foo/hello 路由,~/pages/foo.vue 将优先于 ~/pages/foo/[slug].vue
使用 ~/pages/foo/index.vue~/pages/foo/[slug].vue 来匹配具有不同页面的 /foo/foo/hello

捕获所有路由

如果您需要一个捕获所有路由,您可以通过创建一个类似 [...slug].vue 的文件来实现。这将匹配该路径下的 所有 路由。

pages/[...slug].vue
<template>
  <p>{{ $route.params.slug }}</p>
</template>

导航到 /hello/world 将渲染:

<p>["hello", "world"]</p>

嵌套路由

可以使用 <NuxtPage> 显示 嵌套路由

示例:

Directory Structure
-| pages/
---| parent/
-----| child.vue
---| parent.vue

此文件树将生成以下路由:

[
  {
    path: '/parent',
    component: '~/pages/parent.vue',
    name: 'parent',
    children: [
      {
        path: 'child',
        component: '~/pages/parent/child.vue',
        name: 'parent-child'
      }
    ]
  }
]

要显示 child.vue 组件,您必须将 <NuxtPage> 组件插入到 pages/parent.vue 中:

pages/parent.vue
<template>
  <div>
    <h1>I am the parent view</h1>
    <NuxtPage :foobar="123" />
  </div>
</template>
pages/parent/child.vue
<script setup lang="ts">
const props = defineProps(['foobar'])

console.log(props.foobar)
</script>

子路由键

如果您希望更精细地控制 <NuxtPage> 组件何时重新渲染(例如,用于过渡效果),您可以通过 pageKey prop 传递一个字符串或函数,或者您可以通过 definePageMeta 定义一个 key 值:

pages/parent.vue
<template>
  <div>
    <h1>I am the parent view</h1>
    <NuxtPage :page-key="route => route.fullPath" />
  </div>
</template>

或者,另一种方式:

pages/parent/child.vue
<script setup lang="ts">
definePageMeta({
  key: route => route.fullPath
})
</script>
在 Nuxt 3 中,嵌套路由的子路由键(Child Route Key)主要用于精确控制 <NuxtPage> 组件在路由切换时的重新渲染行为。它的核心作用是解决嵌套路由中父子组件状态管理的复用问题,确保子路由组件能按预期更新。以下是详细解析:

子路由键的核心作用:
  1. 控制组件重新渲染
    • 默认行为:当子路由参数变化时(如 /parent/child1/parent/child2),父组件(parent.vue)不会重新渲染,但子组件会更新。
    • 问题场景:若子路由组件依赖父组件传递的响应式数据(如 Props),或需监听路由参数变化,默认行为可能导致状态未更新。
    • 解决方案:通过设置 key 属性,强制子路由组件在路径变化时完全销毁并重新创建,确保状态同步。
      <!-- 父组件 parent.vue -->
      <template>
        <NuxtPage :page-key="route => route.fullPath" />
      </template>
      
  2. 避免状态残留
    • 动态参数场景:例如 /user/[id],当 id 改变时,若未设置 key,Vue 会复用组件实例,导致 createdmounted 钩子不触发。
    • 键值策略
      • route.fullPath:路径变化即刷新(最严格)。
      • route.params.id:仅当特定参数变化时刷新。
      <!-- 子组件 child.vue -->
      <script setup>
      definePageMeta({
        key: route => route.params.id // 仅 id 变化时重新渲染
      })
      </script>
      

配置方式:
  1. 父组件中通过 <NuxtPage> 设置
    • 适用于统一控制所有子路由的渲染策略。
      <template>
        <NuxtPage :page-key="route => route.params.groupId" />
      </template>
      
  2. 子组件中通过 definePageMeta 设置
    • 更细粒度控制,每个子路由可独立配置。
      <script setup>
      definePageMeta({
        key: route => route.fullPath // 或自定义逻辑
      })
      </script>
      

典型应用场景:
  1. 动态参数依赖
    • 子组件需根据路由参数(如 id)异步加载数据,需确保参数变化时重新请求。
      definePageMeta({
        key: route => route.params.id // id 变化时触发子组件重渲染
      })
      
  2. 状态隔离需求
    • 父子路由共享复杂状态时,避免更新冲突(如表单控件需重置)。
  3. 过渡动画优化
    • 结合 Vue 的 <Transition> 组件,强制重新渲染可使动画生效更流畅。
  4. 路由缓存控制
    • keepalive 元数据配合,精确管理哪些子路由需被缓存:
      definePageMeta({
        keepalive: true,
        key: route => route.query.tab // 按 tab 参数缓存不同实例
      })
      

注意事项:
问题解决方案引用
键值冲突避免父子组件重复设置 key,优先用子组件配置
性能影响避免过度使用 fullPath,可能引发不必要的渲染
SSR 兼容性确保 key 生成逻辑兼容服务端(避免浏览器 API)
嵌套路由多层控制每层都需通过 <NuxtPage> 传递 key

总结:子路由键是 Nuxt 3 嵌套路由中状态管理的核心开关,通过以下策略平衡灵活性与性能:
  • 精确更新:用 route.params.id 等最小化渲染范围。
  • 严格更新:用 route.fullPath 确保任何路径变化都刷新组件。
  • 混合控制:父组件全局策略 + 子组件覆盖局部策略。

实践建议:优先在子组件中通过 definePageMeta 声明 key,仅需关注关键动态参数的变化。对于深层嵌套路由,每层父组件需显式传递 <NuxtPage :page-key="..."> 以避免状态污染。

路由组

在某些情况下,您可能希望将一组路由分组在一起,而不会影响基于文件的路由。为此,您可以将文件放在用括号 () 包裹的文件夹中。

例如:

Directory structure
-| pages/
---| index.vue
---| (marketing)/
-----| about.vue
-----| contact.vue

这将在您的应用程序中生成 //about/contact 页面。marketing 组在 URL 结构中将被忽略。

页面元数据

您可能希望为应用程序中的每个路由定义元数据。您可以使用 definePageMeta 宏来完成此操作,该宏在 <script><script setup> 中都有效:

<script setup lang="ts">
definePageMeta({
  title: 'My home page'
})
</script>

然后可以在应用程序的其余部分通过 route.meta 对象访问此数据。

<script setup lang="ts">
const route = useRoute()

console.log(route.meta.title) // My home page
</script>

如果您使用的是嵌套路由,则来自所有这些路由的页面元数据将合并为一个对象。有关路由元数据的更多信息,请参阅 vue-router 文档

defineEmitsdefineProps(参见 Vue 文档)类似,definePageMeta 是一个 编译器宏。它将被编译掉,因此您无法在组件中引用它。相反,传递给它的元数据将被提升到组件之外。 因此,页面元数据对象不能引用组件。但是,它可以引用导入的绑定以及本地定义的 纯函数

确保不要引用任何响应式数据或导致副作用的函数。这可能会导致意外的行为。
<script setup lang="ts">
import { someData } from '~/utils/example'

function validateIdParam(route) {
  return route.params.id && !isNaN(Number(route.params.id))
}

const title = ref('')

definePageMeta({
  validate: validateIdParam,
  someData,
  title,    // do not do this, the ref will be hoisted out of the component
})
</script>

特殊元数据

当然,欢迎您定义供您在整个应用程序中使用的元数据。但是,使用 definePageMeta 定义的一些元数据具有特定的用途:

alias

您可以定义页面别名。它们允许您从不同的路径访问同一个页面。它可以是字符串或字符串数组,如 vue-router 文档 此处 所定义。

keepalive

如果在 definePageMeta 中设置 keepalive: true,Nuxt 将自动将您的页面包裹在 Vue 组件 中。例如,如果您想在路由更改时保留父路由(具有动态子路由)的页面状态,这可能很有用。

当您的目标是保留父路由的状态时,请使用以下语法:<NuxtPage keepalive />。您还可以设置要传递给 <KeepAlive> 的 props(完整列表请参见 此处)。

您可以在 nuxt.config 中为此属性设置默认值。

key

见上文.

layout

您可以定义用于渲染路由的布局。如果希望以某种方式使其具有响应性,则可以将其设置为 false(禁用任何布局)、字符串或 ref/computed。更多关于布局的信息

layoutTransitionpageTransition

您可以为包裹您的页面和布局的 <transition> 组件定义过渡属性,或者传递 false 以禁用该路由的 <transition> 包裹器。您可以在 此处 查看可以传递的选项列表,或者 阅读更多关于过渡如何工作的信息

您可以在 nuxt.config 中为这些属性设置默认值。

middleware

您可以定义在加载此页面之前应用的中间件。它将与任何匹配的父/子路由中使用的所有其他中间件合并。它可以是字符串、函数(遵循 全局前置守卫模式 的匿名/内联中间件函数)或字符串/函数数组。更多关于命名中间件的信息

name

您可以为此页面的路由定义一个名称。

path

如果您有比文件名更复杂的模式,可以定义路径匹配器。有关更多信息,请参阅 vue-router 文档。

类型化自定义元数据

如果您为页面添加自定义元数据,您可能希望以类型安全的方式进行操作。可以扩充 definePageMeta 接受的对象类型:

index.d.ts
declare module '#app' {
  interface PageMeta {
    pageType?: string
  }
}

// It is always important to ensure you import/export something when augmenting a type
export {}

导航

要在应用程序的页面之间导航,您应该使用 <NuxtLink> 组件。

此组件包含在 Nuxt 中,因此您不必像其他组件一样导入它。

一个指向 pages 文件夹中 index.vue 页面的简单链接:


<template>
  <NuxtLink to="/">Home page</NuxtLink>
</template>
了解更多关于 <NuxtLink> 的用法。

编程式导航

Nuxt 允许通过 navigateTo() 实用方法进行编程式导航。使用此实用方法,您将能够在应用程序中以编程方式导航用户。这对于获取用户输入并在整个应用程序中动态导航他们非常有用。在此示例中,我们有一个名为 navigate() 的简单方法,当用户提交搜索表单时调用该方法。

务必始终 awaitnavigateTo 或通过从函数返回来链式调用其结果。
<script setup lang="ts">
const name = ref('');
const type = ref(1);

function navigate(){
  return navigateTo({
    path: '/search',
    query: {
      name: name.value,
      type: type.value
    }
  })
}
</script>

仅客户端页面

您可以通过为页面添加 .client.vue 后缀将其定义为 仅客户端 组件。此页面的任何内容都不会在服务器上渲染。

仅服务器端页面

您可以通过为页面添加 .server.vue 后缀将其定义为 仅服务器端 组件。虽然您可以使用由 vue-router 控制的客户端导航导航到该页面,但它将自动使用服务器组件进行渲染,这意味着渲染该页面所需的代码不会包含在您的客户端 bundle 中。

仅服务器端页面必须具有单个根元素。(HTML 注释也被认为是元素。)

自定义路由

随着您的应用程序变得越来越大和越来越复杂,您的路由可能需要更高的灵活性。为此,Nuxt 直接公开了路由器、路由和路由器选项,以便以不同的方式进行自定义。

Read more in Docs > Guide > Recipes > Custom Routing.

多个 Pages 目录

默认情况下,您的所有页面都应该位于项目根目录下的一个 pages 目录中。

但是,您可以使用 Nuxt Layers 来创建应用程序页面的分组:

Directory Structure
-| some-app/
---| nuxt.config.ts
---| pages/
-----| app-page.vue
-| nuxt.config.ts
some-app/nuxt.config.ts
// some-app/nuxt.config.ts
export default defineNuxtConfig({
})
nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./some-app'],
})
Read more in Docs > Guide > Going Further > Layers.