路由系统

Nuxt 的路由基于 vue-router,但你几乎不用手写配置。app/pages/ 下每个文件就是一条路由 —— 自动分包、自动预取、自动类型。

文件式路由

app/pages/
├── index.vue          →  /
├── about.vue          →  /about
└── posts/
    ├── index.vue      →  /posts
    └── [id].vue       →  /posts/:id

Nuxt 会生成完整的类型化路由树,<NuxtLink>to 值也会带类型。

动态段

方括号即 params:

<!-- app/pages/posts/[id].vue -->
<script setup lang="ts">
const route = useRoute()
console.log(route.params.id) // string
</script>

多个参数也 OK:app/pages/users/[userId]/posts/[postId].vue/users/:userId/posts/:postId

Catch-allapp/pages/docs/[...slug].vue 会匹配 /docs/a/b/croute.params.slug = ['a', 'b', 'c']

嵌套路由

parent.vueparent/ 目录共存,在父组件里放 <NuxtPage />

<!-- app/pages/settings.vue -->
<template>
  <section>
    <aside><!-- 导航 --></aside>
    <NuxtPage />
  </section>
</template>
app/pages/
├── settings.vue
└── settings/
    ├── profile.vue      →  /settings/profile
    └── billing.vue      →  /settings/billing

<NuxtLink> 导航

应用内跳转一律用 <NuxtLink>

<template>
  <nav>
    <NuxtLink to="/">首页</NuxtLink>
    <NuxtLink to="/about">关于</NuxtLink>
    <NuxtLink :to="{ name: 'posts-id', params: { id: 1 } }">文章 1</NuxtLink>
  </nav>
</template>

它会:

  • 渲染真正的 <a>(对无障碍友好)。
  • 链接进入视口时自动预取组件与 payload。
  • Hydration 之后走客户端跳转,不会整页刷新。
  • 自动加上 .router-link-active / .router-link-exact-active 类名。

想临时关掉预取:

<NuxtLink to="/heavy" :prefetch="false">Heavy page</NuxtLink>

程序式导航

<script setup lang="ts">
const router = useRouter()
function openPost (id: number) {
  router.push(`/posts/${id}`)
}
</script>

在中间件、插件、事件处理器里优先使用 navigateTo()

export default defineNuxtRouteMiddleware((to) => {
  if (!to.path.startsWith('/public')) return navigateTo('/login')
})

路由中间件

三种形态:

  1. 内联:直接写在页面的 definePageMeta({ middleware: (to) => {…} }) 里。
  2. 命名:文件放在 app/middleware/auth.ts,页面手动声明使用。
  3. 全局app/middleware/foo.global.ts,每次导航都会跑。
// app/middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const { user } = useUserSession()
  if (!user.value) return navigateTo('/login')
})
<script setup lang="ts">
definePageMeta({ middleware: 'auth' })
</script>

路由中间件只在 Vue(客户端 & SSR)层执行。服务端/API 请求走 server/middleware/

路由校验

validate 校验参数:

<script setup lang="ts">
definePageMeta({
  validate (route) {
    return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id)
  },
})
</script>

返回假值会触发 404;返回 { status, statusText } 可以自定义响应。

definePageMeta 概览

<script setup lang="ts">
definePageMeta({
  layout: 'admin',          // 指定布局
  middleware: ['auth'],     // 路由中间件
  pageTransition: false,    // 关闭过渡
  validate: (r) => true,    // 校验
  key: (r) => r.path,       // 重渲染策略
})
</script>

/posts/1/posts/2 这种相同路由不同参数的场景下,加 key 可以让组件重新创建。

小结

  • .vue 文件丢进 app/pages/,路由就已经有了。
  • 应用内跳转一律 <NuxtLink>,获得预取与过渡。
  • 鉴权、重定向、守卫交给中间件。
  • definePageMeta 用来声明布局、中间件、校验等。

路由系統

Nuxt 的路由基於 vue-router,但你幾乎毋須手寫設定。app/pages/ 下每個檔案即一條路由 —— 自動分包、自動預取、自動型別。

檔案式路由

app/pages/
├── index.vue          →  /
├── about.vue          →  /about
└── posts/
    ├── index.vue      →  /posts
    └── [id].vue       →  /posts/:id

Nuxt 會產生完整的型別化路由樹,<NuxtLink>to 亦具型別。

動態段

方括號即 params:

<!-- app/pages/posts/[id].vue -->
<script setup lang="ts">
const route = useRoute()
console.log(route.params.id) // string
</script>

多個參數亦可:app/pages/users/[userId]/posts/[postId].vue/users/:userId/posts/:postId

Catch-allapp/pages/docs/[...slug].vue 會匹配 /docs/a/b/croute.params.slug = ['a', 'b', 'c']

巢狀路由

parent.vueparent/ 目錄共存,父元件內使用 <NuxtPage />

<!-- app/pages/settings.vue -->
<template>
  <section>
    <aside><!-- 導覽 --></aside>
    <NuxtPage />
  </section>
</template>
app/pages/
├── settings.vue
└── settings/
    ├── profile.vue      →  /settings/profile
    └── billing.vue      →  /settings/billing

<NuxtLink> 導航

應用內跳轉一律使用 <NuxtLink>

<template>
  <nav>
    <NuxtLink to="/">首頁</NuxtLink>
    <NuxtLink to="/about">關於</NuxtLink>
    <NuxtLink :to="{ name: 'posts-id', params: { id: 1 } }">文章 1</NuxtLink>
  </nav>
</template>

它會:

  • 渲染真正的 <a>(對無障礙友善)。
  • 連結進入視窗時自動預取元件與 payload。
  • Hydration 後走客戶端跳轉,不會整頁重新整理。
  • 自動附加 .router-link-active / .router-link-exact-active 類別。

若要暫時關閉預取:

<NuxtLink to="/heavy" :prefetch="false">Heavy page</NuxtLink>

程式化導航

<script setup lang="ts">
const router = useRouter()
function openPost (id: number) {
  router.push(`/posts/${id}`)
}
</script>

於中介層、外掛、事件處理器中,優先使用 navigateTo()

export default defineNuxtRouteMiddleware((to) => {
  if (!to.path.startsWith('/public')) return navigateTo('/login')
})

路由中介層

三種形態:

  1. 內嵌:直接寫在頁面的 definePageMeta({ middleware: (to) => {…} })
  2. 具名:檔案置於 app/middleware/auth.ts,頁面手動宣告使用。
  3. 全域app/middleware/foo.global.ts,每次導航皆會執行。
// app/middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const { user } = useUserSession()
  if (!user.value) return navigateTo('/login')
})
<script setup lang="ts">
definePageMeta({ middleware: 'auth' })
</script>

路由中介層僅於 Vue(客戶端 & SSR)層執行。伺服器/API 請求請改用 server/middleware/

路由驗證

validate 驗證參數:

<script setup lang="ts">
definePageMeta({
  validate (route) {
    return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id)
  },
})
</script>

回傳假值會觸發 404;回傳 { status, statusText } 可自訂回應。

definePageMeta 一覽

<script setup lang="ts">
definePageMeta({
  layout: 'admin',          // 指定佈局
  middleware: ['auth'],     // 路由中介層
  pageTransition: false,    // 關閉過渡
  validate: (r) => true,    // 驗證
  key: (r) => r.path,       // 重新渲染策略
})
</script>

/posts/1/posts/2 這類同路由不同參數的場景,加上 key 可讓元件重新建立。

小結

  • .vue 丟進 app/pages/,路由即已就位。
  • 應用內跳轉一律 <NuxtLink>,自動享受預取與過渡。
  • 鑑權、重導向、守衛交給中介層。
  • definePageMeta 用於宣告佈局、中介層、驗證等。

Routing

Nuxt's router is built on vue-router, but you almost never configure it by hand. Instead, every file under app/pages/ becomes a route — automatic code-splitting, automatic prefetching, automatic types.

File-based routing

app/pages/
├── index.vue          →  /
├── about.vue          →  /about
└── posts/
    ├── index.vue      →  /posts
    └── [id].vue       →  /posts/:id

Nuxt emits a fully typed route tree, including typed to values for <NuxtLink>.

Dynamic segments

Square brackets turn into params:

<!-- app/pages/posts/[id].vue -->
<script setup lang="ts">
const route = useRoute()
console.log(route.params.id) // string
</script>

Multiple params work too: app/pages/users/[userId]/posts/[postId].vue/users/:userId/posts/:postId.

Catch-all: app/pages/docs/[...slug].vue matches /docs/a/b/c with route.params.slug = ['a', 'b', 'c'].

Nested routes

Pair parent.vue with a parent/ folder. Use <NuxtPage /> inside the parent to render children:

<!-- app/pages/settings.vue -->
<template>
  <section>
    <aside><!-- nav --></aside>
    <NuxtPage />
  </section>
</template>
app/pages/
├── settings.vue
└── settings/
    ├── profile.vue      →  /settings/profile
    └── billing.vue      →  /settings/billing

Navigation with <NuxtLink>

Use <NuxtLink> for every internal navigation:

<template>
  <nav>
    <NuxtLink to="/">Home</NuxtLink>
    <NuxtLink to="/about">About</NuxtLink>
    <NuxtLink :to="{ name: 'posts-id', params: { id: 1 } }">Post 1</NuxtLink>
  </nav>
</template>

Behind the scenes:

  • Renders a real <a> (great for accessibility).
  • Prefetches the component and payload when the link enters the viewport.
  • Performs client-side transitions after hydration — no full reloads.
  • Adds .router-link-active / .router-link-exact-active classes.

Opt out of prefetching selectively:

<NuxtLink to="/heavy" :prefetch="false">Heavy page</NuxtLink>

Programmatic navigation

<script setup lang="ts">
const router = useRouter()
function openPost (id: number) {
  router.push(`/posts/${id}`)
}
</script>

In middleware, plugins, and event handlers prefer navigateTo():

export default defineNuxtRouteMiddleware((to) => {
  if (!to.path.startsWith('/public')) return navigateTo('/login')
})

Route middleware

Three flavors:

  1. Inline — defined right in the page via definePageMeta({ middleware: (to) => {…} }).
  2. Named — file in app/middleware/auth.ts, opt-in per page.
  3. Globalapp/middleware/foo.global.ts runs on every navigation.
// app/middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const { user } = useUserSession()
  if (!user.value) return navigateTo('/login')
})
<script setup lang="ts">
definePageMeta({ middleware: 'auth' })
</script>

Route middleware runs only in the Vue (client & SSR) layer. Server/API requests go through server/middleware/ instead.

Route validation

Reject invalid params with validate:

<script setup lang="ts">
definePageMeta({
  validate (route) {
    return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id)
  },
})
</script>

A falsy result triggers a 404. Return an object { status, statusText } for custom responses.

definePageMeta at a glance

<script setup lang="ts">
definePageMeta({
  layout: 'admin',          // choose layout
  middleware: ['auth'],     // route middleware
  pageTransition: false,    // disable transitions
  validate: (r) => true,    // validation
  key: (r) => r.path,       // re-render strategy
})
</script>

key is useful when navigating between same-route params (/posts/1/posts/2) and you want the component torn down and re-created.

Summary

  • Put .vue files under app/pages/ — you already have routing.
  • Use <NuxtLink> everywhere for prefetching and transitions.
  • Use middleware for auth, redirects, and per-route guards.
  • Use definePageMeta to declare layout, middleware, validation, and more.