中间件
Nuxt 提供了一个可自定义的路由中间件框架,你可以将其用于整个应用,非常适合提取要在导航到特定路由之前运行的代码。
¥Nuxt provides a customizable route middleware framework you can use throughout your application, ideal for extracting code that you want to run before navigating to a particular route.
路由中间件有三种类型:
¥There are three kinds of route middleware:
- 匿名(或内联)路由中间件直接在页面中定义。
- 命名路由中间件,放置在
middleware/
目录中,并在页面上使用时通过异步导入自动加载。 - 全局路由中间件,放置在
middleware/
中(后缀为.global
),并在每次路由更改时自动运行。
前两种路由中间件可以在 definePageMeta
中定义。
¥The first two kinds of route middleware can be defined in definePageMeta
.
myMiddleware
变为 my-middleware
。用法
¥Usage
路由中间件是接收当前路由和下一个路由作为参数的导航守卫。
¥Route middleware are navigation guards that receive the current route and the next route as arguments.
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 提供了两个全局可用的辅助函数,可以直接从中间件返回。
¥Nuxt provides two globally available helpers that can be returned directly from the middleware.
navigateTo
- 重定向到指定的路由abortNavigation
- 中止导航,并显示可选的错误消息。
与 vue-router
中的 导航保护 不同,通用渲染无需传递第三个 next()
参数,重定向或路由取消操作由中间件返回值来处理。
¥Unlike navigation guards from vue-router
, a third next()
argument is not passed, and redirect or route cancellation is handled by returning a value from the middleware.
可能的返回值包括:
¥Possible return values are:
- 什么也没有(简单的
return
或根本没有返回) - 不会阻止导航,并将转到下一个中间件函数(如果有),或完成路由导航。 return navigateTo('/')
- 重定向到指定路径,如果重定向发生在服务器端,则将重定向代码设置为302
已找到return navigateTo('/', { redirectCode: 301 })
- 重定向到指定路径,如果重定向发生在服务器端,则将重定向代码设置为301
永久移动return abortNavigation()
- 停止当前导航return abortNavigation(error)
- 拒绝当前导航并返回错误
中间件顺序
¥Middleware Order
中间件按以下顺序运行:
¥Middleware runs in the following order:
- 全局中间件
- 页面定义的中间件顺序(如果使用数组语法声明了多个中间件)
例如,假设你有以下中间件和组件:
¥For example, assuming you have the following middleware and component:
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
<script setup lang="ts">
definePageMeta({
middleware: [
function (to, from) {
// Custom inline middleware
},
'auth',
],
});
</script>
你可以预期中间件将按以下顺序运行:
¥You can expect the middleware to be run in the following order:
analytics.global.ts
setup.global.ts
- 自定义内联中间件
auth.ts
全局中间件排序
¥Ordering Global Middleware
默认情况下,全局中间件会根据文件名按字母顺序执行。
¥By default, global middleware is executed alphabetically based on the filename.
然而,有时你可能需要定义特定的顺序。例如,在最后一种情况下,setup.global.ts
可能需要在 analytics.global.ts
之前运行。在这种情况下,我们建议在全局中间件前添加 'alphabetical' 编号。
¥However, there may be times you want to define a specific order. For example, in the last scenario, setup.global.ts
may need to run before analytics.global.ts
. In that case, we recommend prefixing global middleware with 'alphabetical' numbering.
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
10.new.global.ts
会排在 2.new.global.ts
之前。这就是为什么示例中在单个数字前加上 0
前缀的原因。中间件运行时
¥When Middleware Runs
如果你的网站是服务端渲染或生成的,则初始页面的中间件将在页面渲染时以及客户端再次执行。如果你的中间件需要浏览器环境,例如,如果你有一个生成的网站、积极缓存响应或想要从本地存储读取值,则可能需要这样做。
¥If your site is server-rendered or generated, middleware for the initial page will be executed both when the page is rendered and then again on the client. This might be needed if your middleware needs a browser environment, such as if you have a generated site, aggressively cache responses, or want to read a value from local storage.
但是,如果你想避免这种情况,可以这样做:
¥However, if you want to avoid this behaviour you can do so:
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
})
即使你在服务器上的中间件中抛出错误并渲染了错误页面,也是如此。中间件仍会在浏览器中运行。
¥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
来检查错误是否正在被处理。动态添加中间件
¥Adding Middleware Dynamically
可以使用 addRouteMiddleware()
辅助函数手动添加全局或命名路由中间件,例如在插件内部添加。
¥It is possible to add global or named route middleware manually using the addRouteMiddleware()
helper function, such as from within a plugin.
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')
})
})
示例
¥Example
-| middleware/
---| auth.ts
在你的页面文件中,你可以引用此路由中间件:
¥In your page file, you can reference this route middleware:
<script setup lang="ts">
definePageMeta({
middleware: ["auth"]
// or middleware: 'auth'
})
</script>
现在,在导航到该页面之前,auth
路由中间件将运行。
¥Now, before navigation to that page can complete, the auth
route middleware will be run.
在构建时设置中间件
¥Setting Middleware at Build Time
无需在每个页面上都使用 definePageMeta
,你可以在 pages:extend
钩子中添加命名路由中间件。
¥Instead of using definePageMeta
on each page, you can add named route middleware within the pages:extend
hook.
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 */ true) {
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)
}
}
})