Nuxt 提供了一个可定制的路由中间件框架,你可以在整个应用程序中使用它,非常适合提取要在导航到特定路由之前运行的代码。
路由中间件有三种类型:
app/middleware/ 目录中,并在页面中使用时通过异步导入自动加载。app/middleware/ 目录中,并带有 .global 后缀,并在每次路由更改时运行。前两种路由中间件可以在 definePageMeta 中定义。
myMiddleware 变为 my-middleware。路由中间件是导航守卫,它将当前路由和下一个路由作为参数接收。
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
// In a real app you would probably not redirect every route to `/`
// however it is important to check `to.path` before redirecting or you
// might get an infinite redirect loop
if (to.path !== '/') {
return navigateTo('/')
}
})
Nuxt 提供了两个全局可用的辅助函数,可以直接从中间件返回。
navigateTo - 重定向到给定的路由。abortNavigation - 中止导航,并带有一个可选的错误消息。与导航守卫来自 vue-router 不同,它不会传递第三个 next() 参数,并且重定向或路由取消通过从中间件返回值来处理。
可能的返回值有:
return 或完全没有返回) - 不会阻止导航,并将移动到下一个中间件函数(如果有),或完成路由导航return navigateTo('/') - 重定向到给定路径,并将重定向代码设置为302 Found如果重定向发生在服务器端return navigateTo('/', { redirectCode: 301 }) - 重定向到给定路径,并将重定向代码设置为301 Moved Permanently如果重定向发生在服务器端return abortNavigation() - 停止当前导航return abortNavigation(error) - 使用错误拒绝当前导航中间件按以下顺序运行:
例如,假设您有以下中间件和组件:
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
<script setup lang="ts">
definePageMeta({
middleware: [
function (to, from) {
// Custom inline middleware
},
'auth',
],
})
</script>
您可以期望中间件按以下顺序运行:
analytics.global.tssetup.global.tsauth.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) => {
// skip middleware on server
if (import.meta.server) {
return
}
// skip middleware on client side entirely
if (import.meta.client) {
return
}
// or only skip middleware on initial client load
const nuxtApp = useNuxtApp()
if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) {
return
}
})
即使您在服务器上的中间件中抛出错误并渲染错误页面,这也是正确的。中间件仍将在浏览器中再次运行。
useError 来检查是否正在处理错误。在中间件中始终使用 to 和 from 参数来访问下一个和上一个路由。在此上下文中应完全避免使用 useRoute() 可组合项。在中间件中没有“当前路由”的概念,因为中间件可以中止导航或重定向到不同的路由。useRoute() 可组合项在此上下文中将始终不准确。
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('this global middleware was added in a plugin and will be run on every route change')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('this named middleware was added in a plugin and would override any existing middleware of the same name')
})
})
-| middleware/
---| auth.ts
在您的页面文件中,您可以引用此路由中间件:
<script setup lang="ts">
definePageMeta({
middleware: ['auth'],
// or middleware: 'auth'
})
</script>
现在,在导航到该页面完成之前,auth 路由中间件将运行。
除了在每个页面上使用 definePageMeta 之外,您还可以在 pages:extend 钩子中添加命名路由中间件。
import type { NuxtPage } from 'nuxt/schema'
export default defineNuxtConfig({
hooks: {
'pages:extend' (pages) {
function setMiddleware (pages: NuxtPage[]) {
for (const page of pages) {
if (/* some condition */ Math.random() > 0.5) {
page.meta ||= {}
// Note that this will override any middleware set in `definePageMeta` in the page
page.meta.middleware = ['named']
}
if (page.children) {
setMiddleware(page.children)
}
}
}
setMiddleware(pages)
},
},
})