Skip to content

插件

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:errorVue 组件错误错误上报
app:error应用级错误全局错误处理
app:chunkErrorJS chunk 加载失败提示用户刷新

注意事项

  1. 自动注册app/plugins/ 下所有 .ts 文件自动注册,无需手动引入
  2. 执行一次:插件只在应用初始化时执行一次
  3. SSR + 客户端:默认两端都执行,用 .client.ts / .server.ts 控制执行环境
  4. 避免重逻辑:插件不应包含耗时操作,会阻塞应用启动
  5. 组合式函数优先:如果功能只在部分组件使用,优先用组合式函数而非插件

知识脉络

text
错误处理 → 你在这里:插件

            ├─→ 下一步:插件模式

            └─→ 相关:核心概念 → 自动导入(插件自动注册机制)

基于 Nuxt 4 官方文档整理编写