---
title: "添加插件、组件及更多内容"
description: "了解如何从您的模块中注入插件、组件、组合函数和服务器路由。"
canonical_url: "https://nuxt.zhcndoc.com/docs/4.x/guide/modules/recipes-basics"
---
# 添加插件、组件及更多内容

> 了解如何从您的模块中注入插件、组件、组合函数和服务器路由。

以下是模块作者常用的一些模式。

## 修改 Nuxt 配置

模块可以读取并修改 Nuxt 配置。以下是一个启用实验性功能的模块示例。

```js
import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // 如果还不存在，我们创建 `experimental` 对象
    nuxt.options.experimental ||= {}
    nuxt.options.experimental.componentIslands = true
  },
})
```

当需要处理更复杂的配置修改时，你应该考虑使用 [defu](https://github.com/unjs/defu)。

::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/extending-and-altering-nuxt-configuration-and-options?friend=nuxt" target="_blank"}
观看 Vue School 视频，了解如何修改 Nuxt 配置。
::

## 向运行时公开选项

由于模块不属于应用运行时的一部分，因此它们的选项也不属于运行时。不过，在许多情况下，你可能需要在运行时代码中访问部分模块选项。我们建议使用 Nuxt 的 [`runtimeConfig`](https://nuxt.zhcndoc.com/docs/4.x/api/nuxt-config#runtimeconfig) 来公开所需配置。

<!-- TODO: 在 #18466（或等价功能）之后更新 -->

```js
import { defineNuxtModule } from '@nuxt/kit'
import { defu } from 'defu'

export default defineNuxtModule({
  setup (options, nuxt) {
    nuxt.options.runtimeConfig.public.myModule = defu(nuxt.options.runtimeConfig.public.myModule, {
      foo: options.foo,
    })
  },
})
```

注意，我们使用 [`defu`](https://github.com/unjs/defu) 来扩展用户提供的公共运行时配置，而非直接覆盖。

然后，你可以在插件、组件或应用中像访问其他运行时配置一样访问模块选项：

```js
import { useRuntimeConfig } from '@nuxt/kit'

const options = useRuntimeConfig().public.myModule
```

::warning
请注意不要在公共运行时配置中暴露任何敏感的模块配置，比如私有 API 密钥，因为它们会最终包含在公共包中。
::

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/going-further/runtime-config"}
::

::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/passing-and-exposing-module-options?friend=nuxt" target="_blank"}
观看 Vue School 视频，了解如何传递和公开 Nuxt 模块选项。
::

## 添加插件

插件是模块添加运行时逻辑的常用方式。你可以使用 `addPlugin` 工具从模块中注册它们。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    // 创建解析器以解析相对路径
    const resolver = createResolver(import.meta.url)

    addPlugin(resolver.resolve('./runtime/plugin'))
  },
})
```

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/going-further/kit"}
::

## 添加组件

如果你的模块需要提供 Vue 组件，你可以使用 `addComponent` 工具将它们添加为 Nuxt 的自动导入组件。

```ts
import { addComponent, createResolver, defineNuxtModule, useRuntimeConfig } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    // 来自 runtime 目录
    addComponent({
      name: 'MySuperComponent', // 在 Vue 模板中使用的组件名称
      export: 'MySuperComponent', // （可选）如果组件是命名导出而非默认导出
      filePath: resolver.resolve('runtime/app/components/MySuperComponent.vue'),
    })

    // 来自库
    addComponent({
      name: 'MyAwesomeComponent', // 在 Vue 模板中使用的组件名称
      export: 'MyAwesomeComponent', // （可选）如果组件是命名导出而非默认导出
      filePath: '@vue/awesome-components',
    })
  },
})
```

或者，你也可以使用 `addComponentsDir` 添加整个目录。

```ts
import { addComponentsDir, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addComponentsDir({
      path: resolver.resolve('runtime/app/components'),
    })
  },
})
```

::tip{icon="i-lucide-lightbulb"}
强烈建议为导出的组件添加前缀，以避免与用户代码或其他模块发生冲突。

  :::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/modules/best-practices#prefix-your-exports"}
  :::
::

::note
请注意，所有通常放置在你的 `app/` 文件夹中的组件、页面、组合函数和其他文件都需要放在 `runtime/app/` 中。这将确保它们能够被正确进行类型检查。
::

## 添加组合函数

如果你的模块需要提供组合函数，你可以使用 `addImports` 工具将它们添加为 Nuxt 的自动导入。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addImports({
      name: 'useComposable', // 组合函数的名称
      as: 'useMyComposable', // （可选）在消费应用中可用的别名
      from: resolver.resolve('runtime/app/composables/useComposable'), // 组合函数路径
    })
  },
})
```

多条目可作为数组传入：

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addImports([
      { name: 'useFirstComposable', from: resolver.resolve('runtime/composables/useFirstComposable') },
      { name: 'useSecondComposable', from: resolver.resolve('runtime/composables/useSecondComposable') },
    ])
  },
})
```

或者，你可以使用 `addImportsDir` 添加整个目录。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addImportsDir(resolver.resolve('runtime/composables'))
  },
})
```

::tip{icon="i-lucide-lightbulb"}
强烈建议为导出的组合函数添加前缀，以避免与用户代码或其他模块发生冲突。

  :::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/modules/best-practices#prefix-your-exports"}
  :::
::

::note
请注意，所有通常放置在你的 `app/` 文件夹中的组件、页面、组合函数和其他文件都需要放在 `runtime/app/` 中。这将确保它们能够被正确进行类型检查。
::

### 添加带键的函数

有时，您可能需要在服务器和客户端之间维护状态一致性。示例包括 Nuxt 内置的 `useState` 或 `useAsyncData` 组合函数。Nuxt 提供了一种注册此类函数以实现自动注入唯一键的方法。

当函数被注册时，如果函数调用时传入的参数少于指定参数数量，Nuxt 的编译器会自动注入一个唯一的键作为额外参数。该键在服务器端渲染和客户端水化之间保持稳定。

::tip
注入的键是基于文件路径和调用位置生成的哈希值。
::

使用 `keyedComposables` 选项注册你的函数：

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    nuxt.options.optimization.keyedComposables.push({
      name: 'useMyState',
      source: resolver.resolve('./runtime/composables/state'),
      argumentLength: 2,
    })
  },
})
```

`keyedComposables` 配置接受一个对象数组，每个对象包含以下属性：

| 属性               | 类型       | 描述                                                   |
| ---------------- | -------- | ---------------------------------------------------- |
| `name`           | `string` | 函数名称。对于默认导出使用 `'default'` （可调用名称将从文件名转换为 camelCase）。 |
| `source`         | `string` | 定义函数的文件的解析路径。支持 Nuxt 别名（`~`、`@` 等）。                  |
| `argumentLength` | `number` | 函数接受的最大参数数。调用时如果参数少于此数量，则注入唯一键。                      |

例如，设置 `argumentLength: 2` 的函数调用示例：

```ts
useMyState() // 被转换成 useMyState('$HJiaryoL2y')
useMyState('myKey') // 被转换成 useMyState('myKey', '$HJiaryoL2y')
useMyState('a', 'b') // 不会转换（已有 2 个参数）
```

::warning
键注入插件会验证每个函数调用的精确解析导入来源。它不支持通过中转导出（桶文件）调用。函数必须从 `source` 属性指定的确切源文件导出。

```ts
// ✅ 有效 - 直接导入匹配配置的源文件
import { useMyState } from 'my-module/runtime/composables/state'

// ❌ 无效 - 通过桶文件重导出的函数
import { useMyState } from 'my-module/runtime/composables' // index.ts 桶文件
```
::

::warning
函数调用必须是静态可分析的。编译器无法为动态或间接调用注入键。

```ts
import { useMyState } from 'my-module/runtime/composables/state'
import * as composables from 'my-module/runtime/composables/state'

// ✅ 有效 - 直接调用函数
useMyState()

// ✅ 有效 - 命名空间导入调用
composables.useMyState()

// ❌ 无效 - 动态属性访问
const name = 'useMyState'
composables[name]()

// ❌ 无效 - 重新赋值给变量
const myFn = useMyState
myFn()

// ❌ 无效 - 作为回调传递
someFunction(useMyState)

// ❌ 无效 - 在嵌套作用域中通过重命名解构
function setup () {
  const { useMyState: localState } = composables
  localState() // 不会转换
}

// ...
```
::

## 添加路由中间件

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    // 添加一个全局路由中间件示例
    addRouteMiddleware({
      global: true,
      name: 'name-of-your-middleware',
      path: resolver.resolve('./runtime/middleware/name-of-your-middleware'),
    })
  },
})
```

## 添加服务端路由

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addServerHandler({
      route: '/api/_my-module/hello',
      handler: resolver.resolve('./runtime/server/api/hello/index.get'),
    })
  },
})
```

你也可以添加动态服务器路由：

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    addServerHandler({
      route: '/api/_my-module/hello/:name',
      handler: resolver.resolve('./runtime/server/api/hello/[name].get'),
    })

    // 或使用通配路由
    addServerHandler({
      route: '/api/_my-module/files/**:path',
      handler: resolver.resolve('./runtime/server/api/files/[...path].get'),
    })
  },
})
```

::tip{icon="i-lucide-lightbulb"}
强烈建议为服务器路由添加前缀，以避免与用户自定义路由冲突。像 `/api/auth`、`/api/login` 或 `/api/user` 这类常用路径可能已被应用占用。

  :::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/modules/best-practices#prefix-your-exports"}
  :::
::

## 添加其他资源

如果你的模块需要提供其他类型的资源，也可以注入。以下是一个通过 Nuxt 的 `css` 数组注入样式表的简单示例模块。

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

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    nuxt.options.css.push(resolver.resolve('./runtime/style.css'))
  },
})
```

下面是一个更高级的示例，通过 [Nitro](https://nuxt.zhcndoc.com/docs/4.x/guide/concepts/server-engine) 的 `publicAssets` 选项公开一个资源文件夹：

```js
import { createResolver, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    const resolver = createResolver(import.meta.url)

    nuxt.hook('nitro:config', (nitroConfig) => {
      nitroConfig.publicAssets ||= []
      nitroConfig.publicAssets.push({
        dir: resolver.resolve('./runtime/public'),
        maxAge: 60 * 60 * 24 * 365, // 1 年
      })
    })
  },
})
```

## 使用其他模块

如果你的模块依赖于其他模块，你可以使用 `moduleDependencies` 选项来指定它们：

```ts
import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  meta: {
    name: 'my-module',
  },
  moduleDependencies: {
    '@nuxtjs/tailwindcss': {},
  },
})
```

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/modules/module-dependencies"}
::


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
