Добавление плагинов, компонентов и прочего
Ниже — типичные приёмы, которыми пользуются авторы модулей.
Изменение конфигурации Nuxt
Модули могут читать и менять конфигурацию Nuxt. Ниже модуль включает экспериментальную возможность.
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
// Создаём объект `experimental`, если его ещё нет
nuxt.options.experimental ||= {}
nuxt.options.experimental.componentIslands = true
},
})
Если нужно менять конфигурацию сложнее, имеет смысл использовать defu.
Проброс опций в рантайм
Модули не входят в рантайм приложения, поэтому и их опции там недоступны. Но часто нужен доступ к настройкам модуля из кода приложения. Рекомендуем отдавать нужные значения через 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-ключи) — они попадут в публичный бандл.Добавление плагинов
Плагины — распространённый способ добавить логику в рантайм. Из модуля их регистрируют утилитой 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'))
},
})
Добавление компонентов
Если модуль должен предоставлять 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'),
})
},
})
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'))
},
})
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 передаётся массив объектов со свойствами:
| Свойство | Тип | Описание |
|---|---|---|
name | string | Имя функции. Для default-экспорта укажите 'default' (вызываемое имя берётся из имени файла в camelCase). |
source | string | Разрешённый путь к файлу с определением функции. Поддерживаются алиасы Nuxt (~, @ и т.д.). |
argumentLength | number | Максимальное число аргументов функции. При меньшем числе аргументов подставляется уникальный ключ. |
Например, при argumentLength: 2:
useMyState() // станет: useMyState('$HJiaryoL2y')
useMyState('myKey') // станет: useMyState('myKey', '$HJiaryoL2y')
useMyState('a', 'b') // не преобразуется (уже 2 аргумента)
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 могут быть уже заняты.Добавление прочих ресурсов
Если модуль должен отдавать другие типы ресурсов, их тоже можно подключить. Ниже простой пример: стили через массив 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 и обеспечивает корректный порядок настройки и слияние конфигурации.