pages

Nuxt 提供基于文件的路由功能,用于在你的 Web 应用中创建路由。
为了减少应用的打包体积,该目录是可选的,这意味着如果你只使用 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>索引页</h1>
</template>

pages/index.vue 文件会映射到应用的 / 路由。

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

app.vue
<template>
  <div>
    <!-- 所有页面共享的标记,例如:导航栏 -->
    <NuxtPage />
  </div>
</template>

页面必须有单个根元素,以支持页面间的路由过渡。HTML 注释也被视为元素。

这意味着当路由被服务器端渲染或静态生成时,会正确显示内容,但在客户端导航切换路由时,过渡会失败,页面将不会被渲染。

以下是单根元素页面的示例:

<template>
  <div>
    <!-- 此页面正确地只有一个根元素 -->
    页面内容
  </div>
</template>

动态路由

如果在文件名中使用方括号包裹的内容,它将被转换为动态路由参数。你可以在文件名或目录中混合多个参数和非动态文本。

如果想让参数变为_可选_,需用双重方括号包裹,比如 ~/pages/[[slug]]/index.vue~/pages/[[slug]].vue,这将匹配 //test 两种路径。

目录结构
-| 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>

如果想在组合式 API 中访问路由,可以使用全局 useRoute 函数,类似选项式 API 中的 this.$route

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

if (route.params.group === 'admins' && !route.params.id) {
  console.log('警告!请确保用户已认证!')
}
</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> 来显示嵌套路由

示例:

目录结构
-| 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 组件,需在 pages/parent.vue 内插入 <NuxtPage> 组件:

pages/parent.vue
<template>
  <div>
    <h1>我是父视图</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 属性传入字符串或函数,或者通过 definePageMeta 定义 key 值:

pages/parent.vue
<template>
  <div>
    <h1>我是父视图</h1>
    <NuxtPage :page-key="route => route.fullPath" />
  </div>
</template>

或者:

pages/parent/child.vue
<script setup lang="ts">
definePageMeta({
  key: route => route.fullPath
})
</script>
Read and edit a live example in Docs > Examples > Routing > Pages.

路由分组

有时你可能想将一组路由归为一组,但不影响基于文件的路由规则。为此,可以将文件放置在用括号() 包裹的文件夹内。

例如:

目录结构
-| pages/
---| index.vue
---| (marketing)/
-----| about.vue
-----| contact.vue

这会生成 //about/contact 页面。括号内的 marketing 组不会出现在 URL 结构中。

页面元信息

你可能想为应用中的每个路由定义元信息,可以通过 definePageMeta 宏实现,支持写在 <script><script setup> 中:

<script setup lang="ts">
definePageMeta({
  title: '我的首页'
})
</script>

该数据可在应用中通过 route.meta 对象获取:

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

console.log(route.meta.title) // 我的首页
</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,    // 不要这么做,ref 会被提升出组件
})
</script>

特殊元信息

当然,你可以定义自用的元信息。以下是部分 definePageMeta 中具有特定用途的元信息:

alias

你可以定义页面的别名,允许通过不同路径访问同一个页面。支持字符串或字符串数组,详见 vue-router 文档

keepalive

若在 definePageMeta 中设置 keepalive: true,Nuxt 会自动用 Vue 的 <KeepAlive> 组件包裹该页面。这在父路由包含动态子路由时保存页面状态很有用。

如果想保留父路由状态,可用 <NuxtPage keepalive />。也可设置传递给 <KeepAlive> 的 props (详见 完整选项列表)。

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

key

参见上文

layout

定义该路由渲染使用的布局。可为 false(禁用布局)、字符串,或响应式的 ref/computed。详情请看 布局相关说明

layoutTransitionpageTransition

可定义包裹页面与布局的 <transition> 组件的过渡属性,或设为 false 禁用该路由的 <transition>。可见 支持的选项列表 ,详情参考 过渡功能介绍

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

middleware

定义加载该页面前运行的中间件。它会与所有匹配父/子路由的中间件合并。可以是字符串、函数(符合全局前置守卫模式的匿名/内联中间件),或字符串/函数数组。更多关于命名中间件

name

为该页面路由定义名称。

path

如果文件名无法表达更复杂的路径匹配模式,可定义路径匹配器。更多信息请参阅 vue-router 文档

props

允许将路由 params 作为 props 传入页面组件。详见 vue-router 文档

自定义元信息的类型定义

你可以用类型安全的方式为页面添加自定义元信息,方法是在 definePageMeta 接受的对象类型上进行扩展:

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

// 扩展类型时确保导入/导出语句以避免模块转为全局
export {}

导航

在应用中切换页面时,推荐使用 <NuxtLink> 组件。

该组件由 Nuxt 内置,无需像普通组件那样手动导入。

示例:链接到 pages 目录下的 index.vue 页面:

<template>
  <NuxtLink to="/">首页</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 后缀,可以定义一个仅服务端页面。虽然你可以通过客户端路由导航访问它,页面会自动作为服务端组件渲染,也就是说渲染该页面的代码不会被包含在客户端包中。

仅服务端页面必须拥有单个根元素。(HTML 注释也会被视为元素)

自定义路由

随着应用越来越大、结构更复杂,路由可能需要更灵活的配置。为此,Nuxt 直接暴露路由器、路由和路由配置选项,支持多种定制方式。

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

多 pages 目录

默认情况下,所有页面应放在项目根目录下的一个 pages 目录内。

不过,你也可以使用 Nuxt Layers 创建页面分组:

目录结构
-| 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.