Nuxt3 中文课程 《实战全栈开发简书》.

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 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 (process.server) return
  // 完全在客户端跳过中间件
  if (process.client) return
  // 或仅在初始客户端加载时跳过中间件
  const nuxtApp = useNuxtApp()
  if (process.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
})

动态添加中间件

可以使用 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.