Skip to content

本地模块

为什么需要本地模块?

当项目中有可复用的功能需要封装时,你有三种选择:

方式能力适用场景
组合式函数封装逻辑简单状态/逻辑复用
插件提供全局方法、初始化全局注册、应用初始化
本地模块添加自动导入、注册组件、修改构建配置跨页面复用的功能包

简单说:插件和组合式函数做不到的事(如添加自动导入、注册全局组件、修改 Nitro 配置),才需要本地模块。

创建本地模块

text
modules/
└── my-module/
    ├── index.ts        # 模块入口(构建时执行)
    └── runtime/
        ├── plugin.ts   # 运行时插件
        └── composables/
            └── useMyFeature.ts

模块入口

ts
// modules/my-module/index.ts
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  meta: {
    name: 'my-module',
    configKey: 'myModule',
  },
  defaults: {
    enabled: true,
  },
  setup(options, nuxt) {
    if (!options.enabled) return

    const { resolve } = createResolver(import.meta.url)

    // 添加插件
    addPlugin(resolve('./runtime/plugin'))

    // 添加自动导入
    nuxt.hook('imports:dirs', (dirs) => {
      dirs.push(resolve('./runtime/composables'))
    })

    // 添加自动导入预设(从 npm 包导入指定函数)
    nuxt.hook('imports:sources', (sources) => {
      sources.push({
        from: 'my-lib',
        imports: ['myFunction'],
      })
    })
  },
})

为什么需要 createResolver

核心原因

模块代码经过构建后,相对路径会失效。createResolver(import.meta.url) 基于模块文件的实际位置创建路径解析器,确保无论构建后代码在哪里,路径都能正确指向源文件。

ts
// ❌ 错误:构建后 ./runtime/plugin 可能解析到错误路径
addPlugin('./runtime/plugin')

// ✅ 正确:createResolver 始终相对于源文件位置解析
const { resolve } = createResolver(import.meta.url)
addPlugin(resolve('./runtime/plugin'))

nuxt.hook 的常用钩子

钩子名用途触发时机
imports:dirs添加自动导入目录构建时,解析自动导入前
imports:sources添加自动导入预设构建时,解析自动导入前
components:dirs添加组件目录构建时,解析组件前
nitro:config修改 Nitro 配置构建时,创建 Nitro 实例前
prepare:types添加类型声明构建时,生成类型前

TIP

这些钩子名来源于 Nuxt 内部的构建流程 你不需要记住所有钩子,按需查阅即可

注册本地模块

ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    // 方式一:路径引用
    './modules/my-module',

    // 方式二:带配置
    ['./modules/my-module', { enabled: true }],
  ],

  // 方式三:通过配置键(对应 meta.configKey)
  myModule: {
    enabled: true,
  },
})

配置合并机制

defaults 中的默认值会被 nuxt.config.ts 中的配置覆盖。即:defaults → 用户配置 → 最终 options

运行时 vs 构建时

INFO

这是理解本地模块最关键的概念

代码位置执行时机可用 API典型用途
index.ts(setup)构建时@nuxt/kit 工具、nuxt.hook注册插件、添加自动导入、修改配置
runtime/ 目录运行时Vue 组合式函数、浏览器/Node API实际功能逻辑
ts
// ❌ 错误:在 setup 中使用运行时 API
setup(options, nuxt) {
  const route = useRoute()    // 报错!构建时没有路由
  const state = useState()    // 报错!构建时没有响应式系统
}

// ✅ 正确:运行时逻辑放在 runtime/ 中
// runtime/composables/useMyFeature.ts
export const useMyFeature = () => {
  const route = useRoute()    // OK,运行时有路由
  const state = useState()    // OK,运行时有响应式系统
}

实战示例:通知模块

一个更实用的本地模块示例,展示模块的完整能力:

text
modules/
└── notification/
    ├── index.ts
    └── runtime/
        ├── plugin.ts
        ├── composables/
        │   └── useNotification.ts
        └── components/
            └── NotificationToast.vue
ts
// modules/notification/index.ts
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  meta: { name: 'notification', configKey: 'notification' },
  defaults: { position: 'top-right' as const, duration: 3000 },
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    addPlugin(resolve('./runtime/plugin'))
    nuxt.hook('imports:dirs', (dirs) => {
      dirs.push(resolve('./runtime/composables'))
    })
    nuxt.hook('components:dirs', (dirs) => {
      dirs.push(resolve('./runtime/components'))
    })

    // 把配置传给运行时
    nuxt.options.appConfig.notification = options
  },
})
ts
// modules/notification/runtime/composables/useNotification.ts
export const useNotification = () => {
  const notifications = ref<{ id: number; message: string; type: string }[]>([])

  function notify(message: string, type = 'info') {
    const id = Date.now()
    notifications.value.push({ id, message, type })
    setTimeout(() => {
      notifications.value = notifications.value.filter(n => n.id !== id)
    }, 3000)
  }

  return { notifications, notify }
}

模块模板

使用 CLI 创建模块模板:

bash
npx nuxi init -t module my-module

TIP

这会创建一个完整的模块开发环境 包含 playground/ 测试项目、TypeScript 配置等

常见问题

问题原因解决方案
模块安装后不生效忘记在 modules 中注册确认 nuxt.config.tsmodules 包含模块路径
resolve 路径报错没有使用 createResolver始终用 createResolver(import.meta.url) 创建解析器
setup 中 useState 报错在构建时使用了运行时 API运行时逻辑放到 runtime/ 目录
配置不生效configKeynuxt.config.ts 中的键名不匹配确保 meta.configKey 与配置键名一致
@nuxt/kit 导入失败未安装 @nuxt/kit本地模块也需要 npm install -D @nuxt/kit

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