# Добавление плагинов, компонентов и прочего

> Узнайте, как подключать плагины, компоненты, композаблы и серверные маршруты из модуля.

Ниже — типичные приёмы, которыми пользуются авторы модулей.

## Изменение конфигурации 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" target="_blank" to="https://vueschool.io/lessons/extending-and-altering-nuxt-configuration-and-options?friend=nuxt">

Посмотрите видео Vue School о расширении и изменении конфигурации и опций Nuxt.

</tip>

## Проброс опций в рантайм

Модули не входят в рантайм приложения, поэтому и их опции там недоступны. Но часто нужен доступ к настройкам модуля из кода приложения. Рекомендуем отдавать нужные значения через [`runtimeConfig`](/docs/3.x/api/nuxt-config#runtimeconfig) Nuxt.

```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), чтобы дополнять публичную `runtimeConfig`, которую задал пользователь, а не перезаписывать её.

Дальше опции модуля доступны в плагине, компоненте или приложении так же, как и остальная конфигурация рантайма:

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

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

<warning>

Не выносите в публичную `runtimeConfig` чувствительные данные модуля (например, приватные API-ключи) — они попадут в публичный бандл.

</warning>

<read-more to="/docs/3.x/guide/going-further/runtime-config">



</read-more>

<tip icon="i-lucide-video" target="_blank" to="https://vueschool.io/lessons/passing-and-exposing-module-options?friend=nuxt">

Посмотрите видео Vue School о передаче и экспонировании опций модуля Nuxt.

</tip>

## Добавление плагинов

Плагины — распространённый способ добавить логику в рантайм. Из модуля их регистрируют утилитой `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="/docs/3.x/guide/going-further/kit">



</read-more>

## Добавление компонентов

Если модуль должен предоставлять Vue-компоненты, используйте `addComponent`, чтобы зарегистрировать их как автоимпорты для Nuxt.

```tstwoslash
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', // (необязательно) если экспорт именованный, а не default
      filePath: resolver.resolve('runtime/app/components/MySuperComponent.vue'),
    })

    // Из библиотеки
    addComponent({
      name: 'MyAwesomeComponent', // имя компонента в шаблонах Vue
      export: 'MyAwesomeComponent', // (необязательно) если экспорт именованный, а не default
      filePath: '@vue/awesome-components',
    })
  },
})
```

Либо добавьте целый каталог через `addComponentsDir`.

```ts
import { addComponentsDir, createResolver, 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="/docs/3.x/guide/modules/best-practices#prefix-your-exports">



</read-more>
</tip>

<note>

Все компоненты, страницы, композаблы и прочие файлы, которые обычно лежали бы в `app/`, для модуля должны находиться в `runtime/app/` — тогда для них корректно сработает проверка типов.

</note>

<note>

Все компоненты, страницы, композаблы и прочие файлы, которые обычно лежали бы в `app/`, для модуля должны находиться в `runtime/app/` — тогда для них корректно сработает проверка типов.

</note>

## Добавление композаблов

Если модуль должен предоставлять композаблы, используйте `addImports`, чтобы зарегистрировать их как автоимпорты.

```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="/docs/3.x/guide/modules/best-practices#prefix-your-exports">



</read-more>
</tip>

<note>

Все компоненты, страницы, композаблы и прочие файлы, которые обычно лежали бы в `app/`, для модуля должны находиться в `runtime/app/` — тогда для них корректно сработает проверка типов.

</note>

### Функции с ключами

Иногда нужно согласовывать состояние между сервером и клиентом. Примеры — встроенные композаблы Nuxt `useState` и `useAsyncData`. Nuxt позволяет регистрировать такие функции для автоматической подстановки ключа.

После регистрации компилятор Nuxt при вызове с меньшим числом аргументов, чем задано, автоматически добавляет уникальный ключ. Он стабилен между SSR и гидратацией на клиенте.

<tip>

Подставляемый ключ — хэш от пути к файлу и места вызова.

</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` передаётся массив объектов со свойствами:

<table>
<thead>
  <tr>
    <th>
      Свойство
    </th>
    
    <th>
      Тип
    </th>
    
    <th>
      Описание
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        name
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Имя функции. Для default-экспорта укажите <code>
        'default'
      </code>
      
       (вызываемое имя берётся из имени файла в camelCase).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        source
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Разрешённый путь к файлу с определением функции. Поддерживаются алиасы Nuxt (<code>
        ~
      </code>
      
      , <code>
        @
      </code>
      
       и т.д.).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        argumentLength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      Максимальное число аргументов функции. При меньшем числе аргументов подставляется уникальный ключ.
    </td>
  </tr>
</tbody>
</table>

Например, при `argumentLength: 2`:

```ts
useMyState() // станет: useMyState('$HJiaryoL2y')
useMyState('myKey') // станет: useMyState('myKey', '$HJiaryoL2y')
useMyState('a', 'b') // не преобразуется (уже 2 аргумента)
```

<warning>

Плагин подстановки ключей сверяет точный разрешённый источник импорта для каждого вызова. Экспорты через barrel-файл (`index.ts` и т.п.) не учитываются. Функция должна экспортироваться из того же файла, что указан в `source`.

```ts
// ✅ Работает — прямой импорт совпадает с настроенным `source`
import { useMyState } from 'my-module/runtime/composables/state'

// ❌ Не сработает — реэкспорт через barrel
import { useMyState } from 'my-module/runtime/composables' // реэкспорт из index.ts (barrel)
```

</warning>

<warning>

Вызов функции должен быть статически анализируемым. Компилятор не подставит ключи при динамических или косвенных вызовах.

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

// ✅ Работает — прямой вызов функции
useMyState()

// ✅ Работает — вызов через namespace import
composables.useMyState()

// ❌ Не сработает — динамический доступ к свойству
const name = 'useMyState'
composables[name]()

// ❌ Не сработает — присвоение в переменную
const myFn = useMyState
myFn()

// ❌ Не сработает — передача как callback
someFunction(useMyState)

// ❌ Не сработает — деструктуризация с переименованием во вложенной области
function setup () {
  const { useMyState: localState } = composables
  localState() // не преобразуется
}

// ...
```

</warning>

<note>

Все компоненты, страницы, композаблы и прочие файлы, которые обычно лежали бы в `app/`, для модуля должны находиться в `runtime/app/` — тогда для них корректно сработает проверка типов.

</note>

## Добавление серверных маршрутов

```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'),
    })

    // Или catch-all маршрут
    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="/docs/3.x/guide/modules/best-practices#prefix-your-exports">



</read-more>
</tip>

## Добавление прочих ресурсов

Если модуль должен отдавать другие типы ресурсов, их тоже можно подключить. Ниже простой пример: стили через массив `css` Nuxt.

```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'))
  },
})
```

Более продвинутый вариант — папка статики через опцию `publicAssets` [Nitro](/docs/3.x/guide/concepts/server-engine):

```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 { createResolver, defineNuxtModule } from '@nuxt/kit'

const resolver = createResolver(import.meta.url)

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: 'my-module',
  },
  moduleDependencies: {
    '@nuxtjs/tailwindcss': {
      // Можно задать ограничение версии модуля
      version: '>=6',
      // Конфигурация, которая должна переопределить `nuxt.options`
      overrides: {
        exposeConfig: true,
      },
      // Конфигурация по умолчанию: перебивает дефолты модуля, но не то,
      // что уже задано в `nuxt.options`
      defaults: {
        config: {
          darkMode: 'class',
          content: {
            files: [
              resolver.resolve('./runtime/components/**/*.{vue,mjs,ts}'),
              resolver.resolve('./runtime/*.{mjs,js,ts}'),
            ],
          },
        },
      },
    },
  },
  setup (options, nuxt) {
    // Подключаем CSS с директивами Tailwind
    nuxt.options.css.push(resolver.resolve('./runtime/assets/styles.css'))
  },
})
```

<callout type="info">

Опция `moduleDependencies` заменяет устаревшую функцию `installModule` и обеспечивает корректный порядок настройки и слияние конфигурации.

</callout>
