Nuxt.js插件系统 #

一、插件概述 #

Nuxt.js 插件系统允许你在应用启动时执行代码,注册全局组件,添加全局方法等。

1.1 插件用途 #

  • 注册全局组件
  • 添加全局方法
  • 初始化第三方库
  • 注入服务
  • 配置全局状态

二、创建插件 #

2.1 基本插件 #

plugins/my-plugin.ts

typescript
export default defineNuxtPlugin((nuxtApp) => {
  console.log('Plugin loaded')
})

2.2 提供全局方法 #

plugins/format.ts

typescript
export default defineNuxtPlugin((nuxtApp) => {
  return {
    provide: {
      formatPrice: (price: number) => `¥${price.toFixed(2)}`,
      formatDate: (date: string) => new Date(date).toLocaleDateString('zh-CN')
    }
  }
})

使用:

vue
<script setup lang="ts">
const { $formatPrice, $formatDate } = useNuxtApp()
</script>

<template>
  <p>{{ $formatPrice(99.99) }}</p>
  <p>{{ $formatDate('2024-01-01') }}</p>
</template>

2.3 类型定义 #

plugins/format.ts

typescript
declare module '#app' {
  interface NuxtApp {
    $formatPrice: (price: number) => string
    $formatDate: (date: string) => string
  }
}

export default defineNuxtPlugin((nuxtApp) => {
  return {
    provide: {
      formatPrice: (price: number) => `¥${price.toFixed(2)}`,
      formatDate: (date: string) => new Date(date).toLocaleDateString('zh-CN')
    }
  }
})

三、插件执行时机 #

3.1 仅客户端执行 #

plugins/client-only.client.ts

typescript
export default defineNuxtPlugin((nuxtApp) => {
  console.log('This only runs on client')
})

3.2 仅服务端执行 #

plugins/server-only.server.ts

typescript
export default defineNuxtPlugin((nuxtApp) => {
  console.log('This only runs on server')
})

3.3 执行顺序 #

插件按字母顺序执行。可以使用数字前缀控制顺序:

text
plugins/
├── 01.first.ts
├── 02.second.ts
└── 03.third.ts

四、插件钩子 #

4.1 应用钩子 #

typescript
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('app:created', () => {
    console.log('App created')
  })
  
  nuxtApp.hook('app:beforeMount', () => {
    console.log('Before mount')
  })
  
  nuxtApp.hook('app:mounted', () => {
    console.log('App mounted')
  })
  
  nuxtApp.hook('app:error', (error) => {
    console.error('App error:', error)
  })
})

4.2 页面钩子 #

typescript
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('page:loading:start', () => {
    console.log('Page loading started')
  })
  
  nuxtApp.hook('page:loading:end', () => {
    console.log('Page loading ended')
  })
  
  nuxtApp.hook('page:transition:finish', () => {
    console.log('Page transition finished')
  })
})

4.3 可用钩子列表 #

钩子 说明
app:created 应用创建
app:beforeMount 挂载前
app:mounted 挂载后
app:error 应用错误
app:error:cleared 错误清除
page:loading:start 页面加载开始
page:loading:end 页面加载结束
page:transition:finish 页面过渡完成
link:prefetch 链接预加载

五、第三方库集成 #

5.1 集成Day.js #

bash
pnpm add dayjs

plugins/dayjs.ts

typescript
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
import relativeTime from 'dayjs/plugin/relativeTime'

dayjs.locale('zh-cn')
dayjs.extend(relativeTime)

export default defineNuxtPlugin((nuxtApp) => {
  return {
    provide: {
      dayjs
    }
  }
})

使用:

vue
<script setup lang="ts">
const { $dayjs } = useNuxtApp()

const formattedDate = $dayjs().format('YYYY-MM-DD')
const relativeTime = $dayjs('2024-01-01').fromNow()
</script>

5.2 集成VueUse #

bash
pnpm add @vueuse/core @vueuse/nuxt

nuxt.config.ts

typescript
export default defineNuxtConfig({
  modules: ['@vueuse/nuxt']
})

5.3 集成Chart.js #

bash
pnpm add chart.js vue-chartjs

plugins/chart.ts

typescript
import { Chart, registerables } from 'chart.js'

export default defineNuxtPlugin((nuxtApp) => {
  Chart.register(...registerables)
  
  return {
    provide: {
      Chart
    }
  }
})

六、全局组件注册 #

6.1 插件注册组件 #

plugins/components.ts

typescript
import MyButton from '~/components/MyButton.vue'
import MyInput from '~/components/MyInput.vue'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.component('MyButton', MyButton)
  nuxtApp.vueApp.component('MyInput', MyInput)
})

6.2 批量注册 #

plugins/components.ts

typescript
const components = import.meta.glob('~/components/global/*.vue', { eager: true })

export default defineNuxtPlugin((nuxtApp) => {
  Object.entries(components).forEach(([path, component]: [string, any]) => {
    const name = path.split('/').pop()?.replace('.vue', '')
    if (name) {
      nuxtApp.vueApp.component(name, component.default)
    }
  })
})

七、全局指令 #

7.1 创建指令 #

plugins/directives.ts

typescript
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.directive('click-outside', {
    mounted(el, binding) {
      el._clickOutside = (event: Event) => {
        if (!(el === event.target || el.contains(event.target))) {
          binding.value()
        }
      }
      document.body.addEventListener('click', el._clickOutside)
    },
    unmounted(el) {
      document.body.removeEventListener('click', el._clickOutside)
    }
  })
})

使用:

vue
<template>
  <div v-click-outside="closeDropdown">
    Dropdown content
  </div>
</template>

7.2 权限指令 #

typescript
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.directive('permission', {
    mounted(el, binding) {
      const { value } = binding
      const { user } = useAuth()
      
      if (!user.value?.permissions?.includes(value)) {
        el.parentNode?.removeChild(el)
      }
    }
  })
})

八、注入服务 #

8.1 API服务 #

plugins/api.ts

typescript
interface ApiService {
  get: <T>(url: string) => Promise<T>
  post: <T>(url: string, data: any) => Promise<T>
  put: <T>(url: string, data: any) => Promise<T>
  delete: <T>(url: string) => Promise<T>
}

declare module '#app' {
  interface NuxtApp {
    $api: ApiService
  }
}

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()
  
  const api: ApiService = {
    async get<T>(url: string): Promise<T> {
      return $fetch(url, { baseURL: config.public.apiBase })
    },
    async post<T>(url: string, data: any): Promise<T> {
      return $fetch(url, {
        method: 'POST',
        body: data,
        baseURL: config.public.apiBase
      })
    },
    async put<T>(url: string, data: any): Promise<T> {
      return $fetch(url, {
        method: 'PUT',
        body: data,
        baseURL: config.public.apiBase
      })
    },
    async delete<T>(url: string): Promise<T> {
      return $fetch(url, {
        method: 'DELETE',
        baseURL: config.public.apiBase
      })
    }
  }
  
  return {
    provide: {
      api
    }
  }
})

使用:

vue
<script setup lang="ts">
const { $api } = useNuxtApp()

const users = await $api.get<User[]>('/users')
</script>

九、最佳实践 #

9.1 插件命名 #

text
plugins/
├── 01.init.ts
├── api.ts
├── dayjs.ts
├── directives.ts
└── analytics.client.ts

9.2 类型安全 #

typescript
interface MyPlugin {
  $myMethod: (arg: string) => void
}

declare module '#app' {
  interface NuxtApp extends MyPlugin {}
}

9.3 懒加载 #

typescript
export default defineNuxtPlugin(async (nuxtApp) => {
  if (import.meta.client) {
    const { default: library } = await import('heavy-library')
    
    return {
      provide: {
        library
      }
    }
  }
})

十、完整示例 #

10.1 通知插件 #

plugins/notification.ts

typescript
interface NotificationOptions {
  type: 'success' | 'error' | 'warning' | 'info'
  message: string
  duration?: number
}

interface NotificationService {
  success: (message: string) => void
  error: (message: string) => void
  warning: (message: string) => void
  info: (message: string) => void
}

declare module '#app' {
  interface NuxtApp {
    $notify: NotificationService
  }
}

export default defineNuxtPlugin((nuxtApp) => {
  const notifications = useState('notifications', () => [])
  
  const add = (options: NotificationOptions) => {
    const id = Date.now()
    const notification = {
      id,
      ...options,
      duration: options.duration || 5000
    }
    
    notifications.value.push(notification)
    
    if (notification.duration > 0) {
      setTimeout(() => {
        const index = notifications.value.findIndex(n => n.id === id)
        if (index > -1) {
          notifications.value.splice(index, 1)
        }
      }, notification.duration)
    }
  }
  
  const notify: NotificationService = {
    success: (message) => add({ type: 'success', message }),
    error: (message) => add({ type: 'error', message }),
    warning: (message) => add({ type: 'warning', message }),
    info: (message) => add({ type: 'info', message })
  }
  
  return {
    provide: {
      notify
    }
  }
})

使用:

vue
<script setup lang="ts">
const { $notify } = useNuxtApp()

const handleSave = async () => {
  try {
    await saveData()
    $notify.success('保存成功!')
  } catch (error) {
    $notify.error('保存失败')
  }
}
</script>

十一、总结 #

本章介绍了 Nuxt.js 插件系统:

  • 创建和注册插件
  • 提供全局方法和服务
  • 插件执行时机控制
  • 使用钩子函数
  • 集成第三方库
  • 注册全局组件和指令

插件系统是扩展 Nuxt.js 功能的核心机制,下一章我们将学习模块系统。

最后更新:2026-03-28