pages

Nuxt 提供基于文件的路由,用于在 Web 应用程序中创建路由。
为了减少应用程序的打包体积,此目录是 可选的,这意味着如果您仅使用 app.vue,将不会包含 vue-router。要强制启用页面系统,请在 nuxt.config 中设置 pages: true,或使用 router.options.ts

使用方法

页面是 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>

如果您想使用 Composition API 访问路由,可以使用全局的 useRoute 函数,它与 Options 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。:br 使用 ~/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>
阅读并编辑实时示例中的内容 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> 的属性(参见 完整列表)。

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

key

见上文

layout

您可以定义用于渲染路由的布局。可以是 false(禁用任何布局)、字符串或 ref/computed(如果您希望以响应式方式处理)。了解更多关于布局的信息

layoutTransitionpageTransition

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

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

middleware

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

name

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

path

如果您需要比文件名所能表达的更复杂的模式,可以定义路径匹配器。参见 vue-router 文档 以获取更多信息。

props

允许将路由 params 作为属性传递给页面组件。参见 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() 方法,在用户提交搜索表单时调用。

请始终确保在 navigateTo 上使用 await 或通过从函数返回来链式调用其结果。
<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 控制)访问该页面,但它将自动使用服务器组件渲染,这意味着渲染页面所需的代码不会包含在客户端打包中。

仅服务器页面必须具有单一根元素。(HTML 注释也被视为元素。)

自定义路由

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

阅读更多 Docs > Guide > Recipes > Custom Routing.

多页面目录

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

然而,您可以使用 Nuxt 层 来创建应用程序页面的分组:

目录结构
-| 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'],
})
阅读更多 Docs > Guide > Going Further > Layers.