pages

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

用法

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

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

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

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

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

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

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

这意味着当路由在服务器端渲染或静态生成时,您将能正确看到其内容;但当您在客户端导航到该路由时,如果页面没有单一根元素,路由间的过渡将失败,且页面不会被渲染。

下面是一些示例来说明具有单一根元素的页面长什么样:

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

动态路由

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

如果您希望参数为可选,则必须使用双重方括号包裹,例如 ~/pages/[[slug]]/index.vue~/pages/[[slug]].vue 将匹配 //test

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

基于上述示例,您可以在组件中通过 $route 对象访问 group/id:

app/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('Warning! Make sure user is authenticated!')
}
</script>
命名的父路由会优先于嵌套的动态路由。对于 /foo/hello 路由,~/pages/foo.vue 会优先于 ~/pages/foo/[slug].vue
要使用不同页面分别匹配 /foo/foo/hello,请使用 ~/pages/foo/index.vue~/pages/foo/[slug].vue

Catch-all 路由

如果您需要一个 catch-all 路由,可以使用名为 [...slug].vue 的文件创建。它将匹配该路径下的所有路由。

app/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 组件,您需要在 app/pages/parent.vue 中插入 <NuxtPage> 组件:

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: String,
})

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

子路由键

如果您想更精细地控制何时重新渲染 <NuxtPage> 组件(例如,用于过渡),可以通过 pageKey 属性传入字符串或函数,或者通过 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>
Read and edit a live example in Docs > 4 X > Examples > Routing > Pages.

路由分组

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

例如:

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

这将在您的应用中生成 //about/contact 页面。对于 URL 结构而言,marketing 分组会被忽略。

页面元数据

您可能希望为应用中的每个路由定义元数据。您可以使用 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 && !Number.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

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

name

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

path

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

props

允许将路由的 params 作为 props 传递给页面组件。有关更多信息,请参阅 vue-router 文档

为自定义元数据添加类型

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

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

// 扩展类型时,确保导入/导出一些内容是很重要的
export {}

导航

在页面之间导航时,应使用 <NuxtLink> 组件。

该组件随 Nuxt 一起包含,因此您无需像其他组件那样导入它。

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

<template>
  <NuxtLink to="/">Home page</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 直接暴露了路由器、路由和路由器选项,以便以不同方式进行自定义。

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

多个 pages 目录

默认情况下,所有页面应位于项目根目录的 app/pages 目录中。

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

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 > 4 X > Guide > Going Further > Layers.