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