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('/')- 重定向到给定路径,如果重定向发生在服务器端,则将重定向代码设置为302Foundreturn navigateTo('/', { redirectCode: 301 })- 重定向到给定路径,如果重定向发生在服务器端,则将重定向代码设置为301Moved Permanently`return 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.tssetup.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()`` 组合式函数。 中间件中**没有"当前路由"的概念**,因为中间件可以中止导航或重定向到不同的路由。useRoute()` 组合式函数在此上下文中将始终不准确。
useRoute() 的组合式函数,即使中间件中没有直接调用,这也可能触发此警告。
这会导致与上述相同的问题,因此你应该构造函数以在中间件中使用时接受路由作为参数。// @errors: 2304
export default defineNuxtRouteMiddleware((to) => {
// 将路由传递给函数以避免在中间件中调用 `useRoute()`
doSomethingWithRoute(to)
// ❌ 这将输出警告,不建议使用
callsRouteInternally()
})
// 提供路由作为参数,以便可以在中间件中正确使用
export function doSomethingWithRoute (route = useRoute()) {
// ...
}
// ❌ 此函数不适合在中间件中使用
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'],
// 或 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 ||= {}
// 请注意,这将覆盖页面 `definePageMeta` 中设置的任何中间件
page.meta.middleware = ['named']
}
if (page.children) {
setMiddleware(page.children)
}
}
}
setMiddleware(pages)
},
},
})