hooks
通过 Nuxt 运行时 Hook 扩展 API 系统行为,实现全局请求拦截、错误处理和 401 自定义处理。
介绍
Movk Nuxt API 系统通过 Nuxt 运行时 Hook 暴露 4 个扩展点,可以在不修改源码的情况下扩展/定制 API 行为。
所有 Hook 类型已通过模块声明自动注册到 RuntimeNuxtHooks,编辑器中 nuxtApp.hook('movk:api:') 可获得完整自动补全。
可用 Hooks
movk:api:request
触发时机: 认证 token 注入完成后,请求实际发送前。
'movk:api:request': (context: FetchContext) => void | Promise<void>
典型用途:添加额外请求头、请求日志、修改请求参数。
plugins/api-logger.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:request', (context) => {
console.log(`[API] ${context.options.method || 'GET'} ${context.request}`)
})
})
movk:api:response
触发时机: 业务状态码校验通过 + 数据解包完成后。
'movk:api:response': (
context: FetchContext & { response: FetchResponse<any> }
) => void | Promise<void>
此时 context.response._data 已经是解包后的业务数据。
典型用途:全局数据转换、响应日志、性能分析。
plugins/api-analytics.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:response', (context) => {
analytics.track('api_success', {
url: context.request,
method: context.options.method || 'GET'
})
})
})
movk:api:error
触发时机: 业务状态码校验失败 或 HTTP 错误(4xx/5xx)。对于 401 错误,在 movk:api:unauthorized 之后触发。
'movk:api:error': (
context: FetchContext & { response: FetchResponse<any> }
) => void | Promise<void>
典型用途:全局错误上报(Sentry 等)、错误分析。
plugins/error-reporter.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:error', (context) => {
// 上报到 Sentry(跳过已处理的 401)
if (context.response.status !== 401) {
Sentry.captureException(new Error(`API Error: ${context.response.status}`), {
extra: {
url: context.request,
data: context.response._data
}
})
}
})
})
movk:api:unauthorized
触发时机: 收到 HTTP 401 响应时,在 movk:api:error 之前触发。
'movk:api:unauthorized': (
context: FetchContext & { response: FetchResponse<any> },
result: { handled: boolean }
) => void | Promise<void>
设置 result.handled = true 可跳过默认的 401 处理(清除 session + 跳转登录页)。
如果多个处理器监听此 Hook,任一处理器设置
result.handled = true 即可阻止默认行为。Token 刷新模式
plugins/token-refresh.ts
export default defineNuxtPlugin((nuxtApp) => {
let isRefreshing = false
nuxtApp.hook('movk:api:unauthorized', async (_context, result) => {
if (isRefreshing) return
isRefreshing = true
try {
// 尝试刷新 token
await $fetch('/api/auth/refresh', { method: 'POST' })
// 刷新 session 状态
const { fetch: fetchSession } = useUserSession()
await fetchSession()
// 标记已处理,跳过默认 401 行为
result.handled = true
}
catch {
// 刷新失败,走默认流程(清 session + 跳转登录)
}
finally {
isRefreshing = false
}
})
})
注册方式
在 Nuxt 插件中(推荐)
适合全局逻辑,应用启动时自动注册:
plugins/api-hooks.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:request', (context) => {
// 全局请求处理
})
nuxtApp.hook('movk:api:error', (context) => {
// 全局错误处理
})
})
在组件中
适合特定页面的局部逻辑:
<script setup>
const nuxtApp = useNuxtApp()
// 仅在当前页面生效
const unregister = nuxtApp.hook('movk:api:error', (context) => {
// 页面级别的错误处理
})
onUnmounted(() => {
unregister() // 组件销毁时清理
})
</script>
在组件中注册 Hook 时,务必通过
onUnmounted 调用返回的卸载函数以避免内存泄漏。执行顺序
请求发起
│
├── 认证 token 注入
├── Debug 日志
├── movk:api:request <-- 用户扩展点
│
├── 网络请求发送 ─────────────>
│
├── HTTP 2xx 响应
│ ├── 业务成功 (code in successCodes)
│ │ ├── 数据解包 (提取 dataKey)
│ │ ├── movk:api:response <-- 用户扩展点
│ │ └── 成功 Toast
│ │
│ └── 业务失败
│ ├── movk:api:error <-- 用户扩展点
│ ├── 错误 Toast
│ └── throw ApiError
│
└── HTTP 4xx/5xx 响应
├── [401] movk:api:unauthorized <-- 用户扩展点
│ └── (未 handled) 清 session + 跳转登录
├── movk:api:error <-- 用户扩展点
└── 错误 Toast
TypeScript 支持
Hook 类型已通过模块声明自动注册:
src/runtime/types/module.ts
declare module 'nuxt/app' {
interface RuntimeNuxtHooks {
'movk:api:request': (context: FetchContext) => void | Promise<void>
'movk:api:response': (context: FetchContext & { response: FetchResponse<any> }) => void | Promise<void>
'movk:api:error': (context: FetchContext & { response: FetchResponse<any> }) => void | Promise<void>
'movk:api:unauthorized': (
context: FetchContext & { response: FetchResponse<any> },
result: { handled: boolean }
) => void | Promise<void>
}
}
无需额外配置,nuxtApp.hook('movk:api:...') 即可获得完整的类型推断和自动补全。