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

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

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

Изменение конфигурации Nuxt

Модули могут читать и менять конфигурацию Nuxt. Ниже модуль включает экспериментальную возможность.

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // Создаём объект `experimental`, если его ещё нет
    nuxt.options.experimental ||= {}
    nuxt.options.experimental.componentIslands = true
  },
})

Если нужно менять конфигурацию сложнее, имеет смысл использовать defu.

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

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

Модули не входят в рантайм приложения, поэтому и их опции там недоступны. Но часто нужен доступ к настройкам модуля из кода приложения. Рекомендуем отдавать нужные значения через runtimeConfig Nuxt.

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

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

import { useRuntimeConfig } from '@nuxt/kit'

const options = useRuntimeConfig().public.myModule
Не выносите в публичную runtimeConfig чувствительные данные модуля (например, приватные API-ключи) — они попадут в публичный бандл.
Узнать больше Docs > 3 X > Guide > Going Further > Runtime Config.
Посмотрите видео Vue School о передаче и экспонировании опций модуля Nuxt.

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

Плагины — распространённый способ добавить логику в рантайм. Из модуля их регистрируют утилитой addPlugin.

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

export default defineNuxtModule({
  setup (options, nuxt) {
    // Резолвер для относительных путей
    const resolver = createResolver(import.meta.url)

    addPlugin(resolver.resolve('./runtime/plugin'))
  },
})
Узнать больше Docs > 3 X > Guide > Going Further > Kit.

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

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

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.

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'),
    })
  },
})
Настоятельно рекомендуем добавлять префикс к экспортам, чтобы не пересекаться с кодом пользователя и другими модулями.
Узнать больше Docs > 3 X > Guide > Modules > Best Practices#prefix Your Exports.
Все компоненты, страницы, композаблы и прочие файлы, которые обычно лежали бы в app/, для модуля должны находиться в runtime/app/ — тогда для них корректно сработает проверка типов.
Все компоненты, страницы, композаблы и прочие файлы, которые обычно лежали бы в app/, для модуля должны находиться в runtime/app/ — тогда для них корректно сработает проверка типов.

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

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

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'), // путь к композаблу
    })
  },
})

Несколько записей можно передать массивом:

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.

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

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

    addImportsDir(resolver.resolve('runtime/composables'))
  },
})
Настоятельно рекомендуем добавлять префикс к экспортам, чтобы не пересекаться с кодом пользователя и другими модулями.
Узнать больше Docs > 3 X > Guide > Modules > Best Practices#prefix Your Exports.
Все компоненты, страницы, композаблы и прочие файлы, которые обычно лежали бы в app/, для модуля должны находиться в runtime/app/ — тогда для них корректно сработает проверка типов.

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

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

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

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

Зарегистрируйте функцию через опцию keyedComposables:

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

СвойствоТипОписание
namestringИмя функции. Для default-экспорта укажите 'default' (вызываемое имя берётся из имени файла в camelCase).
sourcestringРазрешённый путь к файлу с определением функции. Поддерживаются алиасы Nuxt (~, @ и т.д.).
argumentLengthnumberМаксимальное число аргументов функции. При меньшем числе аргументов подставляется уникальный ключ.

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

useMyState() // станет: useMyState('$HJiaryoL2y')
useMyState('myKey') // станет: useMyState('myKey', '$HJiaryoL2y')
useMyState('a', 'b') // не преобразуется (уже 2 аргумента)
Плагин подстановки ключей сверяет точный разрешённый источник импорта для каждого вызова. Экспорты через barrel-файл (index.ts и т.п.) не учитываются. Функция должна экспортироваться из того же файла, что указан в source.
// ✅ Работает — прямой импорт совпадает с настроенным `source`
import { useMyState } from 'my-module/runtime/composables/state'

// ❌ Не сработает — реэкспорт через barrel
import { useMyState } from 'my-module/runtime/composables' // реэкспорт из index.ts (barrel)
Вызов функции должен быть статически анализируемым. Компилятор не подставит ключи при динамических или косвенных вызовах.
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() // не преобразуется
}

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

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

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

Можно добавить и динамический серверный маршрут:

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'),
    })
  },
})
Настоятельно рекомендуем задавать префикс серверным маршрутам, чтобы не пересекаться с маршрутами приложения. Пути вроде /api/auth, /api/login или /api/user могут быть уже заняты.
Узнать больше Docs > 3 X > Guide > Modules > Best Practices#prefix Your Exports.

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

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

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:

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. Так надёжнее задаются зависимости с ограничениями версий и слиянием конфигурации:

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'))
  },
})
Опция moduleDependencies заменяет устаревшую функцию installModule и обеспечивает корректный порядок настройки и слияние конфигурации.