pages

Nuxt 提供基于文件的路由,用于在 Web 应用中创建路由。
为了减小应用的打包体积,该目录是可选的,这意味着如果你仅使用Vue Routerapp.vue,则不会包含此目录。要强制启用页面系统,请在 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>
    <!-- Markup shared across all pages, ex: NavBar -->
    <NuxtPage />
  </div>
</template>

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

这意味着当路由在服务端渲染或静态生成时,你可以正常看到内容;但在客户端路由导航期间,路由间的过渡会失败,且你会发现路由无法被渲染。

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

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

动态路由

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

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

目录结构
-| 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 (Composition API) 访问路由,可以使用全局的 useRoute 函数,它允许你像在选项式 API (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 匹配不同的页面。

捕获所有路由

如果你需要一个捕获所有路由 (catch-all route),可以通过创建名为 [...slug].vue 的文件来实现。它将匹配该路径下的所有路由。

app/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 组件,你必须在 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>

子路由键 (Child Route Keys)

如果你想更细致地控制 <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>
Docs > 4 X > Examples > Routing > Pages 中阅读并编辑实时示例。

路由组 (Route Groups)

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

例如

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

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

访问路由组

路由组会自动包含在路由元数据中,表现为 route.meta.groups。这使你可以在组件中访问组信息,用于条件逻辑、样式或其他目的。

pages/(marketing)/about.vue
<script setup lang="ts">
const route = useRoute()

console.log(route.meta.groups) // Output: ['marketing']
</script>

<template>
  <div>
    <p v-if="route.meta.groups?.includes('marketing')">
      This is a marketing page
    </p>
  </div>
</template>

页面元数据

你可能希望为应用中的每个路由定义元数据。可以使用 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 是一个编译器宏。它会在编译阶段被移除,因此你不能在组件内部引用它。相反,传递给它的元数据将被提升到组件之外。因此,页面 meta 对象不能引用组件本身。但是,它可以引用导入的绑定,以及本地定义的纯函数

请确保不要引用任何会导致副作用的响应式数据或函数。这可能导致不可预知的行为。
<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, // do not do this, the ref will be hoisted out of the component
})
</script>

特殊元数据

当然,欢迎你在应用中定义自己的元数据。但一些使用 definePageMeta 定义的元数据具有特定用途。

alias

你可以定义页面别名。它们允许你通过不同的路径访问同一个页面。它可以是字符串或字符串数组,如vue-router 文档中所述.

keepalive

Nuxt 会自动将你的页面包裹在Vue <KeepAlive> 组件中如果你在 definePageMeta 中设置了 keepalive: true。这在某些场景下很有用,例如在具有动态子路由的父路由中,如果你想在路由更改时保留页面状态。

如果你希望为父路由保留状态,请使用以下语法:<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
  }
}

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

要在应用的不同页面之间导航,你应该使用 <NuxtLink> 组件。

此组件包含在 Nuxt 中,因此不需要像其他组件那样导入它。

指向 app/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 后缀将页面定义为 仅服务端。虽然你可以通过客户端导航导航到该页面,它将自动以服务端组件进行渲染,这意味着渲染页面所需的代码将不会出现在你的客户端打包文件中。

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

自定义路由

随着应用变得越来越大且复杂,路由可能需要更高的灵活性。因此,Nuxt 以多种方式直接公开了 router、routes 和 router 选项以供自定义。

阅读更多内容:文档 > 4 X > 指南 > 配方 > 自定义路由

多个页面目录

默认情况下,所有的页面应位于项目根目录的 app/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'],
})
阅读更多内容:文档 > 4 X > 指南 > 深入学习 > Layers