插件
app/plugins/ 目录下的文件自动注册为 Nuxt 插件。插件在应用初始化时执行一次,用于注册全局功能、初始化第三方库、提供全局方法等。
插件 vs 中间件 vs 组合式函数
| 概念 | 执行时机 | 作用 | 示例 |
|---|---|---|---|
| 插件 | 应用初始化时(一次) | 注册全局功能 | API 封装、第三方库、指令 |
| 路由中间件 | 每次路由切换 | 路由守卫 | 认证检查、权限控制 |
| 组合式函数 | 组件调用时 | 可复用逻辑 | useFetch、useState |
| 服务插件 | 服务启动时 | 服务端初始化 | 数据库连接 |
何时用插件?
- 需要在应用启动时执行一次的逻辑
- 需要全局注册的功能(指令、组件、过滤器)
- 需要通过
provide/inject共享的全局方法 - 需要监听应用级钩子(路由变化、错误等)
简单记忆
启动时做一次 + 全局可用 → 插件;组件中按需调用 → 组合式函数。
基本用法
ts
// app/plugins/my-plugin.ts
export default defineNuxtPlugin((nuxtApp) => {
console.log('插件已加载')
// 提供全局方法
return {
provide: {
myMethod: () => 'Hello from plugin',
},
}
})在组件中使用
ts
const { $myMethod } = useNuxtApp()
console.log($myMethod()) // 'Hello from plugin'类型扩展
为 provide 的方法添加 TypeScript 类型声明:
ts
// app/types/index.d.ts
declare module '#app' {
interface NuxtApp {
$myMethod: () => string
}
}
export {}provide 的命名规则
- 插件中:
provide: { myMethod } - 使用时:
$myMethod(自动添加$前缀) - 类型声明:
$myMethod
插件注册顺序
按文件名字母顺序注册,使用数字前缀控制顺序:
text
plugins/
├── 01.init.ts → 第一个执行(初始化配置)
├── 02.auth.ts → 第二个执行(依赖初始化配置)
├── api.ts → 按字母顺序
└── analytics.ts执行顺序的重要性
- 如果插件 B 依赖插件 A 的
provide,A 必须先执行 - 使用数字前缀可以明确控制顺序
- 插件支持
async/await,下一个插件会等待上一个完成
常见插件示例
API 封装
ts
// app/plugins/api.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const api = $fetch.create({
baseURL: config.public.apiBase,
onRequest({ options }) {
const token = useCookie('auth-token')
if (token.value) {
options.headers.set('Authorization', `Bearer ${token.value}`)
}
},
onResponseError({ response }) {
if (response.status === 401) {
navigateTo('/login')
}
},
})
return {
provide: {
api,
},
}
})使用:
ts
const { $api } = useNuxtApp()
// GET
const users = await $api('/users')
// POST
const user = await $api('/users', {
method: 'POST',
body: { name: 'Alice' },
})插件 vs 组合式函数封装 API
- 插件:在应用启动时创建一次
$fetch实例,全局共享 - 组合式函数:每次调用时创建实例
推荐用组合式函数(useApiFetch / createUseFetch)
——更灵活、更容易测试。插件适合需要全局单例的场景。
Google Analytics
ts
// app/plugins/analytics.client.ts
export default defineNuxtPlugin(() => {
const router = useRouter()
router.afterEach((to) => {
if (import.meta.client) {
window.gtag?.('config', 'GA_MEASUREMENT_ID', {
page_path: to.fullPath,
})
}
})
}).client.ts 后缀
确保只在客户端执行(详见插件模式)。GA 等统计代码依赖 window,不能在 SSR 时执行。
全局指令
ts
// app/plugins/directives.ts
export default defineNuxtPlugin((nuxtApp) => {
// 自动聚焦指令
nuxtApp.vueApp.directive('focus', {
mounted(el) {
el.focus()
},
})
// 点击外部指令
nuxtApp.vueApp.directive('click-outside', {
mounted(el, binding) {
el.__clickOutside = (event: Event) => {
if (!(el === event.target || el.contains(event.target))) {
binding.value()
}
}
document.addEventListener('click', el.__clickOutside)
},
unmounted(el) {
document.removeEventListener('click', el.__clickOutside)
},
})
})使用:
vue
<input v-focus placeholder="自动聚焦" />
<div v-click-outside="closeMenu">...</div>Vue I18n
ts
// app/plugins/i18n.ts
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
legacy: false,
locale: 'zh',
messages: {
zh: { hello: '你好' },
en: { hello: 'Hello' },
},
})
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(i18n)
})nuxtApp.vueApp.use()
在插件中安装 Vue 插件。Nuxt 的插件系统会确保在 Vue 应用创建后才执行插件。
provide / inject 模式
插件中 provide 的数据可以在所有组件中通过 useNuxtApp 获取:
ts
// 插件
export default defineNuxtPlugin(() => {
return {
provide: {
dateFormat: (date: Date) => date.toLocaleDateString('zh-CN'),
formatCurrency: (amount: number) => `¥${amount.toFixed(2)}`,
notify: (message: string) => {
// 通知逻辑
},
},
}
})
// 组件
const { $dateFormat, $formatCurrency, $notify } = useNuxtApp()provide/inject 的优势
- 全局可用:所有组件都能访问
- 类型安全:通过类型声明获得自动补全
- 单例模式:插件只在应用启动时创建一次
INFO
️ 不要过度使用 provide:如果只在部分组件中使用 推荐用组合式函数
钩子
在插件中使用 Nuxt 钩子,监听应用生命周期事件:
ts
export default defineNuxtPlugin((nuxtApp) => {
// 页面加载开始
nuxtApp.hook('page:start', () => {
console.log('页面开始加载')
})
// 页面加载完成
nuxtApp.hook('page:finish', () => {
console.log('页面加载完成')
})
// 应用挂载完成(仅客户端)
nuxtApp.hook('app:mounted', () => {
console.log('App mounted')
})
// Vue 错误
nuxtApp.hook('vue:error', (error) => {
console.error('Vue error:', error)
reportToSentry(error)
})
})常用 Nuxt 钩子
| 钩子 | 触发时机 | 适用场景 |
|---|---|---|
page:start | 页面加载开始 | 显示 loading |
page:finish | 页面加载完成 | 隐藏 loading |
app:mounted | 应用挂载完成 | 初始化客户端逻辑 |
vue:error | Vue 组件错误 | 错误上报 |
app:error | 应用级错误 | 全局错误处理 |
app:chunkError | JS chunk 加载失败 | 提示用户刷新 |
注意事项
- 自动注册:
app/plugins/下所有.ts文件自动注册,无需手动引入 - 执行一次:插件只在应用初始化时执行一次
- SSR + 客户端:默认两端都执行,用
.client.ts/.server.ts控制执行环境 - 避免重逻辑:插件不应包含耗时操作,会阻塞应用启动
- 组合式函数优先:如果功能只在部分组件使用,优先用组合式函数而非插件
知识脉络
text
错误处理 → 你在这里:插件
│
├─→ 下一步:插件模式
│
└─→ 相关:核心概念 → 自动导入(插件自动注册机制)