Skip to content

Nuxt Kit

为什么需要 Nuxt Kit?

模块开发需要操作 Nuxt 内部 API(添加插件、注册组件、修改配置等),直接操作这些 API 容易出错且可能因版本升级而失效。Nuxt Kit 封装了稳定的公共 API,让你编写的模块跨版本兼容。

什么时候需要 Nuxt Kit?

只有开发 Nuxt 模块时才需要。日常项目开发(写页面、组件、组合式函数)不需要安装 @nuxt/kit

bash
npm install -D @nuxt/kit

核心工具

defineNuxtModule

定义 Nuxt 模块——每个模块的入口:

ts
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  meta: {
    name: 'my-module',
    configKey: 'myModule',
  },
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)
    addPlugin(resolve('./runtime/plugin'))
  },
})

createResolver

为什么必须用 createResolver

模块代码经过构建后,相对路径会失效。createResolver 基于源文件实际位置解析路径,确保路径始终正确。

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

resolve('./runtime/plugin')    // 解析为绝对路径
resolve('~/app/pages')          // 解析项目路径

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

添加资源

addPlugin

注册运行时插件,插件会在应用启动时自动执行:

ts
addPlugin(resolve('./runtime/plugin'))

addComponent

注册全局组件,使用时不需要 import

ts
addComponent({
  name: 'MyButton',       // 组件名,使用时写 <MyButton>
  filePath: resolve('./runtime/components/Button.vue'),
  mode: 'client',          // 可选:仅客户端加载
})

addComposable

注册全局组合式函数,使用时不需要 import

ts
addComposable({
  name: 'useMyFeature',    // 直接使用 useMyFeature()
  filePath: resolve('./runtime/composables/useMyFeature.ts'),
  exported: 'useMyFeature', // 导出的函数名
})

TIP

addComposablenuxt.hook('imports:dirs') 的区别:前者注册单个函数 后者注册整个目录(目录下所有导出自动注册)。单个函数更精确,整个目录更方便

addLayout

注册布局:

ts
addLayout({
  name: 'custom',
  filePath: resolve('./runtime/layouts/custom.vue'),
})

addServerHandler

注册服务端路由:

ts
addServerHandler({
  route: '/api/my-module/health',
  handler: resolve('./runtime/server/health'),
  method: 'get',  // 可选:限制 HTTP 方法
})

钩子工具

钩子是什么?

Nuxt 构建过程中会在特定时机触发钩子事件,模块通过监听这些事件来修改 Nuxt 的行为。例如,在解析自动导入前添加自定义目录。

添加自动导入

ts
// 方式一:添加整个目录(目录下所有导出自动注册)
nuxt.hook('imports:dirs', (dirs) => {
  dirs.push(resolve('./runtime/composables'))
})

// 方式二:从 npm 包导入指定函数
nuxt.hook('imports:sources', (sources) => {
  sources.push({
    from: 'my-lib',
    imports: ['myFunction', 'anotherFunction'],
  })
})

添加组件目录

ts
nuxt.hook('components:dirs', (dirs) => {
  dirs.push({
    path: resolve('./runtime/components'),
    prefix: 'My',  // 组件名前缀,如 MyButton
  })
})

修改 Nitro 配置

ts
nuxt.hook('nitro:config', (nitroConfig) => {
  nitroConfig.externals?.push(resolve('./runtime/server'))
})

模板工具

addTemplate

生成虚拟模块文件,在构建时动态创建代码:

ts
const template = addTemplate({
  filename: 'my-module-options.mjs',
  write: true,  // 写入 .nuxt/ 目录
  getContents: () => {
    return `export default ${JSON.stringify(options)}`
  },
})
// template.dst 是生成文件的路径,可以在其他地方引用

addTemplate vs addTypeTemplate

addTemplate 生成 JS/TS 模块文件,addTypeTemplate 生成 .d.ts 类型声明文件。前者是运行时使用的,后者是为了 IDE 类型提示。

addTypeTemplate

ts
addTypeTemplate({
  filename: 'types/my-module.d.ts',
  getContents: () => {
    return `declare module '#app' {
      interface NuxtApp {
        $myModule: MyModuleInstance
      }
    }`
  },
})

其他工具

函数说明典型场景
useNuxt获取 Nuxt 实例setup 外访问 Nuxt 配置
useLogger创建日志器模块开发时输出调试信息
isNuxt2 / isNuxt3检查 Nuxt 版本兼容 Nuxt 2/3 的模块
getNuxtVersion获取 Nuxt 版本号版本判断
checkNuxtCompatibility检查兼容性模块安装时检查版本
resolveModule解析模块路径在模块中引用其他 npm 包
installModule安装其他模块模块依赖另一个模块时使用

installModule 示例

ts
// 模块 A 依赖模块 B,自动安装
setup(options, nuxt) {
  installModule('@nuxtjs/tailwindcss')
  installModule('@pinia/nuxt')
}

完整模块示例

一个日志模块的完整实现,展示 Kit 工具的综合使用:

ts
// src/module.ts
import { defineNuxtModule, addPlugin, addComposable, addServerHandler, createResolver } from '@nuxt/kit'

export interface ModuleOptions {
  endpoint: string
  level: 'debug' | 'info' | 'warn' | 'error'
}

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: 'nuxt-logger',
    configKey: 'logger',
    compatibility: { nuxt: '^3.0.0 || ^4.0.0' },
  },
  defaults: {
    endpoint: '/api/log',
    level: 'info',
  },
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    // 注册运行时插件
    addPlugin(resolve('./runtime/plugin'))

    // 注册组合式函数
    addComposable({
      name: 'useLogger',
      filePath: resolve('./runtime/composables/useLogger'),
    })

    // 注册服务端路由
    addServerHandler({
      route: options.endpoint,
      handler: resolve('./runtime/server/log'),
      method: 'post',
    })

    // 传递配置到运行时
    nuxt.options.appConfig.logger = options
  },
})

常见问题

问题原因解决方案
createResolver 路径错误没有传 import.meta.url始终使用 createResolver(import.meta.url)
addTemplate 后找不到文件没有设置 write: true需要写入时设置 write: true
钩子中异步操作报错nuxt.hook 回调中使用了 await 但未声明为 async确保异步钩子回调标记为 async
installModule 循环依赖模块 A 安装模块 B,B 又安装 A检查模块依赖关系,避免循环

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