pages
app.vue
,将不会包含 vue-router
。要强制使用页面系统,可以在 nuxt.config
中设置 pages: true
,或拥有一个 app/router.options.ts
文件。使用方法
页面是 Vue 组件,可以使用 Nuxt 支持的任何有效扩展名(默认包括 .vue
、.js
、.jsx
、.mjs
、.ts
或 .tsx
)。
Nuxt 会为 ~/pages/
目录中的每个页面自动创建路由。
<template>
<h1>首页</h1>
</template>
// https://vuejs.org/guide/extras/render-function.html
export default defineComponent({
render () {
return h('h1', '首页')
}
})
// https://nuxt.com/docs/examples/advanced/jsx
// https://vuejs.org/guide/extras/render-function.html#jsx-tsx
export default defineComponent({
render () {
return <h1>首页</h1>
}
})
pages/index.vue
文件将被映射到应用的 /
路由。
如果你使用 app.vue
,请确保使用 <NuxtPage/>
组件来显示当前页面:
<template>
<div>
<!-- 所有页面共享的标记,例如导航栏 -->
<NuxtPage />
</div>
</template>
页面必须具有单一根元素,以便在页面之间进行路由过渡。HTML 注释也被视为元素。
这意味着,当路由在服务端渲染或静态生成时,你将能够正确看到其内容,但在客户端导航期间导航到该路由时,路由过渡将失败,你会发现路由不会被渲染。
以下是一些示例,说明具有单一根元素的页面是什么样子:
<template>
<div>
<!-- 此页面正确地只有一个单一根元素 -->
页面内容
</div>
</template>
<template>
<!-- 由于此注释,此页面在客户端导航期间路由更改时不会渲染 -->
<div>页面内容</div>
</template>
<template>
<div>此页面</div>
<div>具有多个根元素</div>
<div>在客户端导航期间路由更改时不会渲染</div>
</template>
动态路由
如果你在文件名或目录中使用方括号 []
,它将被转换为动态路由 参数。你可以在文件名或目录中混合使用多个参数,甚至是非动态文本。
如果希望参数是_可选的_,必须用双括号 [[]]
括起来,例如 ~/pages/[[slug]]/index.vue
或 ~/pages/[[slug]].vue
将同时匹配 /
和 /test
。
-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue
在上述示例中,你可以通过组件内的 $route
对象访问 group
和 id
:
<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
。:br 使用 ~/pages/foo/index.vue
和 ~/pages/foo/[slug].vue
来分别匹配 /foo
和 /foo/hello
,以使用不同的页面。捕获所有路由
如果你需要一个捕获所有路由,可以创建一个文件名如 [...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>
组件:
<template>
<div>
<h1>我是父视图</h1>
<NuxtPage :foobar="123" />
</div>
</template>
<script setup lang="ts">
const props = defineProps(['foobar'])
console.log(props.foobar)
</script>
子路由键
如果你希望更精细地控制 <NuxtPage>
组件的重新渲染时机(例如为了过渡效果),可以通过 pageKey
属性传递字符串或函数,或者通过 definePageMeta
定义 key
值:
<template>
<div>
<h1>我是父视图</h1>
<NuxtPage :page-key="route => route.fullPath" />
</div>
</template>
或者:
<script setup lang="ts">
definePageMeta({
key: route => route.fullPath
})
</script>
路由组
在某些情况下,你可能希望将一组路由分组,但不影响基于文件的路由。为此,你可以将文件放在以括号 (
和 )
包裹的文件夹中。
例如:
-| 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 文档。
与 defineEmits
或 defineProps
类似(参见 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(如果需要响应式)。了解更多关于布局的信息。
layoutTransition
和 pageTransition
你可以为包裹页面和布局的 <transition>
组件定义过渡属性,或者传递 false
以禁用该路由的 <transition>
包裹。选项列表见此处,或阅读更多关于过渡的说明。
你可以在 nuxt.config
中为这些属性设置默认值。
middleware
你可以定义在加载页面之前应用的中间件。它将与任何匹配的父/子路由使用的所有其他中间件合并。可以是字符串、函数(遵循 全局前置守卫模式 的匿名/内联中间件函数)或字符串/函数数组。了解更多关于命名中间件的信息。
name
你可以为此页面的路由定义一个名称。
path
如果文件名无法表达复杂的模式,你可以定义路径匹配器。更多信息见 vue-router 文档。
props
允许将路由 params
作为 props 传递给页面组件。更多信息见 vue-router 文档。
自定义元数据的类型定义
如果你为页面添加自定义元数据,可能希望以类型安全的方式进行。可以通过扩展 definePageMeta
接受的对象类型来实现:
declare module '#app' {
interface PageMeta {
pageType?: string
}
}
// 增强类型时始终需要确保导入/导出一个内容
export {}
导航
要在应用的页面之间导航,应使用 <NuxtLink>
组件。
此组件随 Nuxt 提供,因此无需像其他组件一样导入它。
一个简单的指向 pages
文件夹中 index.vue
页面的链接:
<template>
<NuxtLink to="/">首页</NuxtLink>
</template>
编程式导航
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 直接暴露了路由器、路由和路由器选项,以便以不同方式进行自定义。
多个页面目录
默认情况下,所有页面都应位于项目根目录的 pages
目录中。
然而,你可以使用 Nuxt 层 来创建应用的页面分组:
-| some-app/
---| nuxt.config.ts
---| pages/
-----| app-page.vue
-| nuxt.config.ts
// some-app/nuxt.config.ts
export default defineNuxtConfig({
})
export default defineNuxtConfig({
extends: ['./some-app'],
})
::