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
})

This is true even if you throw an error in your middleware on the server, and an error page is rendered. The middleware will still run again in the browser.

渲染错误页面是一个完全独立的页面加载,这意味着任何注册的中间件将再次运行。您可以在中间件中使用 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.

在构建时设置中间件

您可以在 pages:extend 钩子中添加命名路由中间件,而不是在每个页面上使用 definePageMeta

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)
    }
  }
})