middleware

Nuxt 提供中间件,用于在导航到特定路由之前运行代码。

Nuxt 提供了一个可定制的 路由中间件 框架,你可以在整个应用中使用,非常适合用于在导航到特定路由之前需要执行的代码提取。

路由中间件有三种类型:

  1. 匿名(或内联)路由中间件 直接在页面内定义。
  2. 命名路由中间件 放置在 middleware/ 目录下,并在页面上使用时通过异步导入自动加载。
  3. 全局路由中间件 放置在 middleware/ 目录下,文件名带有 .global 后缀,并在每次路由改变时运行。

前两种类型的路由中间件可以在 definePageMeta 中定义。

中间件名称会被标准化为 kebab-case:myMiddleware 变为 my-middleware
路由中间件在 Nuxt 应用的 Vue 部分运行。尽管名称相似,但它们与 服务器中间件 完全不同,后者在你的应用的 Nitro 服务器部分运行。

使用方法

路由中间件是导航守卫,接收当前路由和下一个路由作为参数。

middleware/my-middleware.ts
export default 
defineNuxtRouteMiddleware
((
to
,
from
) => {
if (
to
.
params
.
id
=== '1') {
return
abortNavigation
()
} // 在实际应用中,你可能不会将每个路由都重定向到 `/` // 但是,在重定向之前检查 `to.path` 是非常重要的 // 否则你可能会遇到无限重定向循环 if (
to
.
path
!== '/') {
return
navigateTo
('/')
} })

Nuxt 提供了两个全局可用的辅助函数,可以直接从中间件返回。

  1. navigateTo - 重定向到给定的路由
  2. abortNavigation - 终止当前导航,可选地附带错误信息。

与来自 vue-router导航守卫 不同,这里没有第三个 next() 参数,重定向或取消路由导航是通过从中间件返回值来处理的

可能的返回值包括:

  • 无(简单的 return 或根本不返回)- 不阻止导航,并将移动到下一个中间件函数(如果有的话),或完成路由导航
  • return navigateTo('/') - 重定向到给定路径,并在服务器端发生重定向时设置重定向代码为 302 Found
  • return navigateTo('/', { redirectCode: 301 }) - 重定向到给定路径,并在服务器端发生重定向时设置重定向代码为 301 Moved Permanently
  • return abortNavigation() - 停止当前导航
  • return abortNavigation(error) - 以错误拒绝当前导航
Read more in Docs > API > Utils > Navigate To.
Read more in Docs > API > Utils > Abort Navigation.
我们建议使用上述助手函数进行重定向或停止导航。虽然在 vue-router 文档 描述的其他可能的返回值可能工作,但在未来可能会有破坏性更改。

中间件顺序

中间件按以下顺序运行:

  1. 全局中间件
  2. 页面定义的中间件顺序(如果有多个中间件使用数组语法声明)

例如,假设你有以下中间件和组件:

middleware/ 目录
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
pages/profile.vue
<script setup lang="ts">
definePageMeta({
  middleware: [
    function (to, from) {
      // 自定义内联中间件
    },
    'auth',
  ],
});
</script>

你可以期待中间件按以下顺序运行:

  1. analytics.global.ts
  2. setup.global.ts
  3. 自定义内联中间件
  4. auth.ts

全局中间件排序

默认情况下,全局中间件根据文件名按字母顺序执行。

然而,有时你可能想定义一个特定的顺序。例如,在最后一个场景中,setup.global.ts 可能需要在 analytics.global.ts 之前运行。在这种情况下,我们建议在全局中间件前加上“字母”编号。

目录结构
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
如果你对“字母”编号不熟悉,请记住文件名是作为字符串而不是数字值排序的。例如,10.new.global.ts 将在 2.new.global.ts 之前。这就是为什么示例在单数字前加上 0 的原因。

中间件何时运行

如果您的站点是服务器渲染或生成的,初始页面的中间件将在页面渲染时以及客户端再次运行。这可能是必要的,如果您的中间件需要浏览器环境,比如如果您有一个生成的站点、积极缓存响应或想要从本地存储读取一个值。

但是,如果您想避免这种行为,您可以这样做:

middleware/example.ts
export default 
defineNuxtRouteMiddleware
(
to
=> {
// 在服务器上跳过中间件 if (import.meta.
server
) return
// 完全在客户端跳过中间件 if (import.meta.
client
) return
// 或仅在客户端初始加载时跳过中间件 const
nuxtApp
=
useNuxtApp
()
if (import.meta.
client
&&
nuxtApp
.
isHydrating
&&
nuxtApp
.
payload
.
serverRendered
) return
})
渲染错误页面是一个完全独立的页面加载过程,意味着任何注册的中间件都将再次运行。你可以在中间件中使用 useError 来检查是否正在处理错误。

动态添加中间件

可以使用助手函数 addRouteMiddleware() 手动添加全局或命名路由中间件,例如从插件内部。

export default 
defineNuxtPlugin
(() => {
addRouteMiddleware
('global-test', () => {
console
.
log
('这个全局中间件是在插件中添加的,将在每次路由改变时运行')
}, {
global
: true })
addRouteMiddleware
('named-test', () => {
console
.
log
('这个命名中间件是在插件中添加的,会覆盖任何同名的现有中间件')
}) })

示例

目录结构
-| middleware/
---| auth.ts

在你的页面文件中,你可以引用这个路由中间件:

<script setup lang="ts">
definePageMeta({
  middleware: ["auth"]
  // 或 middleware: 'auth'
})
</script>

现在,在导航到该页面之前,auth 路由中间件将被运行。

Read and edit a live example in Docs > Examples > Routing > Middleware.

构建时设置中间件

除了在每个页面上使用 definePageMeta 外,你还可以在 pages:extend 钩子中添加命名路由中间件。

nuxt.config.ts
import type { 
NuxtPage
} from 'nuxt/schema'
export default
defineNuxtConfig
({
hooks
: {
'pages:extend' (
pages
) {
function
setMiddleware
(
pages
:
NuxtPage
[]) {
for (const
page
of
pages
) {
if (/* 某些条件 */ true) {
page
.
meta
||= {}
// 注意,这将覆盖页面中 `definePageMeta` 设置的任何中间件
page
.
meta
.middleware = ['named']
} if (
page
.
children
) {
setMiddleware
(
page
.
children
)
} } }
setMiddleware
(
pages
)
} } })