middleware
Nuxt 提供一个可自定义的 路由中间件 框架,您可以在整个应用程序中使用,适合提取您希望在导航到特定路由之前运行的代码。
有三种类型的路由中间件:
- 匿名(或内联)路由中间件直接在页面内定义。
- 命名路由中间件,放置在
app/middleware/
中,当在页面上使用时,通过异步导入自动加载。 - 全局路由中间件,放置在
app/middleware/
中并带有.global
后缀,在每次路由更改时运行。
头两种类型的路由中间件可以在 definePageMeta
中定义。
myMiddleware
变为 my-middleware
。使用
路由中间件是接受当前路由和下一个路由作为参数的导航守卫。
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
// 在真实应用中,您可能不会将每个路由重定向到 `/`
// 但在重定向之前检查 `to.path` 是重要的,否则你
// 可能会得到一个无限重定向循环
if (to.path !== '/') {
return navigateTo('/')
}
})
Nuxt 提供两个可以从中间件直接返回的全局可用助手。
navigateTo
- 重定向到给定路由abortNavigation
- 中止导航,并可选提供错误消息。
与 vue-router
中的 导航守卫 不同,不会传递第三个 next()
参数,并且 重定向或路由取消通过从中间件返回值来处理。
可能的返回值有:
- 什么也不返回(一个简单的
return
或根本不返回) - 不会阻止导航,将移动到下一个中间件函数(如果有)或完成路由导航 return navigateTo('/')
- 重定向到给定路径,如果重定向发生在服务器端,将重定向代码设置为302
Foundreturn navigateTo('/', { redirectCode: 301 })
- 重定向到给定路径,如果重定向发生在服务器端,将重定向代码设置为301
Moved Permanentlyreturn abortNavigation()
- 停止当前导航return abortNavigation(error)
- 以错误拒绝当前导航
中间件顺序
中间件按以下顺序运行:
- 全局中间件
- 页面定义的中间件顺序(如果有多个使用数组语法声明的中间件)
例如,假设您有以下中间件和组件:
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
<script setup lang="ts">
definePageMeta({
middleware: [
function (to, from) {
// 自定义内联中间件
},
'auth',
],
});
</script>
您可以期待中间件按如下顺序运行:
analytics.global.ts
setup.global.ts
- 自定义内联中间件
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
。中间件运行时机
如果您的网站是服务器渲染或生成的,初始页面的中间件将在页面渲染时和客户端上再次执行。如果您的中间件需要浏览器环境,比如如果您有生成的网站、积极缓存响应,或者想从本地存储读取值时,这可能是必要的。
但是,如果您想避免这种行为,您可以这样做:
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
来检查是否正在处理错误。在中间件中访问路由
在中间件中始终使用 to
和 from
参数来访问下一个和上一个路由。避免在此上下文中使用 useRoute()
组合式 API。
在中间件中没有“当前路由”的概念,因为中间件可以中止导航或重定向到不同的路由。在此上下文中,useRoute()
组合式 API 始终是不准确的。
useRoute()
的可组合函数,这可能会触发此警告,即使在您的中间件中没有直接调用。这会导致与上述相同的问题,因此您应该在中间件中使用时将路由作为参数传递给您的函数。export default defineNuxtRouteMiddleware(to => {
// passing the route to the function to avoid calling `useRoute()` in middleware
doSomethingWithRoute(to)
// ❌ this will output a warning and is NOT recommended
callsRouteInternally()
})
// providing the route as an argument so that it can be used in middleware correctly
export function doSomethingWithRoute(route = useRoute()) {
// ...
}
// ❌ this function is not suitable for use in middleware
export function callsRouteInternally() {
const route = useRoute()
// ...
}
动态添加中间件
可以通过 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
路由中间件。
在构建时设置中间件
您可以在 pages:extend
钩子中添加命名路由中间件,而不是在每个页面上使用 definePageMeta
。
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)
}
}
})