Nuxt 生态 精选推荐

Nuxt 3 插件系统与自动导入

HTMLPAGE 团队
11 分钟阅读

完整讲解 Nuxt 3 的插件系统、自动导入、全局 API 注入、第三方插件集成等高级功能,提升开发效率。

#Nuxt 3 #插件系统 #自动导入 #全局 API #开发效率

Nuxt 3 插件系统与自动导入

Nuxt 3 的插件系统和自动导入功能可以显著提升开发效率。本文深度讲解这两个核心功能。

1. 插件系统基础

什么是插件

插件是在应用启动前执行的代码,用于:
├─ 全局注册组件、指令、方法
├─ 集成第三方库
├─ 初始化应用状态
├─ 监听全局事件
└─ 扩展 Vue 应用能力

创建基础插件

// plugins/my-plugin.ts
export default defineNuxtPlugin((nuxtApp) => {
  console.log('插件加载中...')
  
  // 返回插件提供的内容
  return {
    provide: {
      // 全局方法
      myMethod: () => 'Hello World',
      
      // 全局属性
      version: '1.0.0'
    }
  }
})

// 在组件中使用
export default {
  setup() {
    const { $myMethod, $version } = useNuxtApp()
    
    console.log($myMethod())  // "Hello World"
    console.log($version)     // "1.0.0"
  }
}

2. 常用插件模式

模式 1: 全局状态管理

// plugins/init-store.ts
export default defineNuxtPlugin(() => {
  const authStore = useAuthStore()
  const settingsStore = useSettingsStore()
  
  // 应用启动时初始化
  return {
    provide: {
      stores: {
        auth: authStore,
        settings: settingsStore
      }
    }
  }
})

模式 2: API 客户端

// plugins/api-client.ts
import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin((nuxtApp) => {
  // 创建 API 客户端
  const apiClient = {
    async get(url: string, options?: any) {
      return $fetch(url, options)
    },
    async post(url: string, data: any, options?: any) {
      return $fetch(url, {
        method: 'POST',
        body: data,
        ...options
      })
    }
  }
  
  return {
    provide: {
      api: apiClient
    }
  }
})

// 使用
const { $api } = useNuxtApp()
const data = await $api.get('/api/users')

模式 3: 全局指令

// plugins/directives.ts
export default defineNuxtPlugin((nuxtApp) => {
  // v-focus 指令: 自动聚焦输入框
  nuxtApp.vueApp.directive('focus', {
    mounted(el: HTMLElement) {
      el.focus()
    }
  })
  
  // v-click-outside 指令
  nuxtApp.vueApp.directive('click-outside', {
    mounted(el: HTMLElement, binding) {
      el.clickOutsideEvent = (event: Event) => {
        if (!(el as any).contains(event.target)) {
          binding.value(event)
        }
      }
      document.addEventListener('click', (el as any).clickOutsideEvent)
    },
    unmounted(el: HTMLElement) {
      document.removeEventListener('click', (el as any).clickOutsideEvent)
    }
  })
})

// 使用
<template>
  <input v-focus />
  <div v-click-outside="closeMenu">...</div>
</template>

模式 4: 第三方库集成

// plugins/vue-toast.ts
import VueToast from 'vue-toast-notification'
import 'vue-toast-notification/dist/theme-default.css'

export default defineNuxtPlugin((nuxtApp) => {
  // 注册第三方组件库
  nuxtApp.vueApp.use(VueToast)
  
  // 也可以只注册全局方法
  const $toast = (message: string, options?: any) => {
    // 自定义 toast 实现
    console.log(message)
  }
  
  return {
    provide: {
      toast: $toast
    }
  }
})

// 使用
const { $toast } = useNuxtApp()
$toast('操作成功!')

模式 5: 拦截器

// plugins/request-interceptor.ts
export default defineNuxtPlugin((nuxtApp) => {
  const interceptors = {
    request: [],
    response: [],
    error: []
  }
  
  // 注册请求拦截器
  const addRequestInterceptor = (handler: (config: any) => any) => {
    interceptors.request.push(handler)
  }
  
  // 注册响应拦截器
  const addResponseInterceptor = (handler: (response: any) => any) => {
    interceptors.response.push(handler)
  }
  
  // 注册错误拦截器
  const addErrorInterceptor = (handler: (error: any) => any) => {
    interceptors.error.push(handler)
  }
  
  return {
    provide: {
      interceptors,
      addRequestInterceptor,
      addResponseInterceptor,
      addErrorInterceptor
    }
  }
})

3. 自动导入 (Auto-imports)

什么是自动导入

自动导入解决的问题:
├─ 无需手动 import 常用的 composables
├─ 无需手动 import 常用的工具函数
├─ 无需手动 import Nuxt 提供的方法
└─ 减少代码重复

例子:
// ❌ 手动导入 (繁琐)
import { useState, useRouter, useRoute } from 'vue'
import { useAsyncData, useRouter } from '#app'
import { useAuthStore } from '~/stores/auth'

// ✅ 自动导入 (简洁)
// 直接使用,无需导入!
const count = useState('count', () => 0)
const router = useRouter()
const authStore = useAuthStore()

配置自动导入

// nuxt.config.ts
export default defineNuxtConfig({
  imports: {
    // 自动导入特定的 composable
    dirs: [
      'composables',
      'composables/**/index.ts'
    ]
  },
  
  // 自动导入 @vueuse/core 中的所有方法
  modules: [
    ['@vueuse/nuxt', {
      // useWindowSize 等方法会自动导入
    }]
  ]
})

自动导入的内容

// ✅ Nuxt 自动导入的全局方法

// Vue 3
- ref, reactive, computed, watch
- onMounted, onUnmounted, onUpdated
- useSlots, useAttrs, useAsyncData

// Nuxt 3
- useRouter, useRoute, useHead
- useFetch, useAsyncData, useLazyAsyncData
- navigateTo, definePageMeta, defineRouteRules
- useState, useCookie, useRequestHeaders

// 你的 composables/
- useCounter (自动从 composables/useCounter.ts)
- useAuthStore (自动从 composables/useAuthStore.ts)

创建可自动导入的 Composable

// composables/useCounter.ts
export const useCounter = () => {
  const count = useState('count', () => 0)
  
  const increment = () => count.value++
  const decrement = () => count.value--
  
  return {
    count,
    increment,
    decrement
  }
}

// 在任何组件中直接使用,无需导入!
<script setup lang="ts">
const { count, increment } = useCounter()
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

组织自动导入

composables/
├── useCounter.ts       → 自动导入为 useCounter
├── useAuth/
│   └── index.ts        → 自动导入为 useAuth
├── useAuth/
│   ├── useLogin.ts     → 自动导入为 useLogin (或 useAuthUseLogin)
│   └── useLogout.ts    → 自动导入为 useLogout
└── api/
    ├── useUsers.ts     → 自动导入为 useUsers
    └── useProducts.ts  → 自动导入为 useProducts

4. 自动导入组件

组件自动注册

components/
├── Button.vue          → 自动注册为 <Button />
├── Card.vue            → 自动注册为 <Card />
│
├── UI/
│   ├── Input.vue       → 自动注册为 <UIInput />
│   └── Modal.vue       → 自动注册为 <UIModal />
│
└── Form/
    ├── Input.vue       → 自动注册为 <FormInput />
    └── Select.vue      → 自动注册为 <FormSelect />
<!-- ✅ 直接使用,无需导入 -->
<template>
  <div>
    <Button>点击</Button>
    <UIInput placeholder="输入..." />
    <FormSelect :options="options" />
  </div>
</template>

配置组件导入

// nuxt.config.ts
export default defineNuxtConfig({
  components: {
    // 仅自动注册特定目录的组件
    dirs: [
      '~/components',
      {
        path: '~/components/ui',
        prefix: 'UI'  // UI 前缀
      }
    ]
  }
})

5. 自动导入工具函数

创建工具库

// utils/helpers.ts
export const formatDate = (date: Date) => {
  return new Intl.DateTimeFormat('zh-CN').format(date)
}

export const formatCurrency = (amount: number) => {
  return new Intl.NumberFormat('zh-CN', {
    style: 'currency',
    currency: 'CNY'
  }).format(amount)
}

export const debounce = (func: Function, delay: number) => {
  let timer: any
  return (...args: any[]) => {
    clearTimeout(timer)
    timer = setTimeout(() => func(...args), delay)
  }
}

// 在任何组件中直接使用
<script setup lang="ts">
const dateStr = formatDate(new Date())
const price = formatCurrency(99.99)
</script>

6. 插件和自动导入最佳实践

// ✅ 最佳实践

// 1. 命名约定
// ✅ 插件: plugins/[name].ts
// ✅ Composable: composables/use[Name].ts
// ✅ 组件: components/[Name].vue
// ✅ 工具: utils/[name].ts

// 2. 组织结构
composables/
  ├── useAuth.ts           (认证相关)
  ├── useData.ts           (数据获取)
  └── useSocial/
      ├── index.ts
      ├── useTwitter.ts
      └── useWeibo.ts

// 3. 避免命名冲突
// ✅ 好: 明确的前缀
useAuthStore
useDataFetch
useUIModal

// ❌ 避免: 模糊的名称
useStore    (哪个 store?)
useFetch    (什么数据?)

// 4. 按需加载
// ✅ 好: 只在需要时加载
plugins: [
  { src: '~/plugins/google-analytics.ts', mode: 'client' }
]

// ❌ 避免: 加载所有插件
plugins: ['~/plugins/*.ts']

7. 性能考虑

// 插件加载策略

// 1. 条件加载
export default defineNuxtPlugin((nuxtApp) => {
  // 仅在浏览器中加载
  if (process.server) return
  
  // 初始化客户端功能
})

// 2. 懒加载
export default defineNuxtPlugin(async (nuxtApp) => {
  // 异步加载,不阻塞应用初始化
  const module = await import('~/libs/heavy-library')
  
  return {
    provide: {
      heavyLib: module
    }
  }
})

// 3. 条件导入
if (process.env.NODE_ENV === 'development') {
  // 仅开发环境加载调试工具
}

总结

Nuxt 3 的插件和自动导入系统的核心优势:

特性优势
插件系统灵活扩展应用能力
自动导入减少代码重复
组件自动注册提升开发效率
类型安全TypeScript 完整支持
按需加载优化性能

相关资源