middleware
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.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) => {
// 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)
},
},
})