components (компоненты)
Nuxt автоматически импортирует компоненты из этой директории (вместе с компонентами из модулей).
-| components/
---| AppHeader.vue
---| AppFooter.vue
<template>
<div>
<AppHeader />
<NuxtPage />
<AppFooter />
</div>
</template>
Имена компонентов
Для компонентов во вложенных папках имя строится из пути и имени файла, повторяющиеся сегменты убираются:
-| components/
---| base/
-----| foo/
-------| Button.vue
Имя компонента: <BaseFooButton />.
Button.vue в BaseFooButton.vue.Чтобы автоимпорт использовал только имя файла без пути, задайте pathPrefix: false в расширенной форме конфига:
export default defineNuxtConfig({
components: [
{
path: '~/components',
pathPrefix: false, },
],
})
Как в Nuxt 2: ~/components/Some/MyComponent.vue будет доступен как <MyComponent>, а не <SomeMyComponent>.
Динамические компоненты
Для синтаксиса Vue <component :is="someComputedComponent"> нужен хелпер resolveComponent или импорт из #components с передачей в проп is.
<script setup lang="ts">
import { SomeComponent } from '#components'
const MyButton = resolveComponent('MyButton')
</script>
<template>
<component :is="clickable ? MyButton : 'div'" />
<component :is="SomeComponent" />
</template>
resolveComponent передавайте только литеральную строку с именем компонента, не переменную. Строка анализируется на этапе компиляции.Можно зарегистрировать все компоненты глобально (не рекомендуется) — тогда для каждого создаётся отдельный асинхронный чанк.
export default defineNuxtConfig({
components: {
+ global: true,
+ dirs: ['~/components']
},
})
Компоненты можно сделать глобальными выборочно: положить их в ~/components/global или использовать суффикс .global.vue. Учитывайте, что каждый глобальный компонент — отдельный чанк.
global может задаваться отдельно для каждой директории компонентов.Динамический импорт
Для ленивой загрузки добавьте префикс Lazy к имени компонента. Код компонента подгрузится только когда он понадобится.
<script setup lang="ts">
const show = ref(false)
</script>
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button
v-if="!show"
@click="show = true"
>
Show List
</button>
</div>
</template>
Отложенная (ленивая) гидрация
Ленивые компоненты помогают контролировать размер чанков, но не всегда улучшают производительность в рантайме, если загружаются сразу. Отложенная гидрация позволяет включать интерактивность компонентов только при появлении в зоне видимости или когда браузер освободится.
Стратегии гидрации
У Nuxt есть встроенные стратегии; для одного ленивого компонента можно выбрать только одну.
hydrate-never).#components не работает.hydrate-on-visible
Гидрация при появлении в viewport.
<template>
<div>
<LazyMyComponent hydrate-on-visible />
</div>
</template>
hydrate-on-idle
Гидрация в момент простоя браузера. Можно передать число — максимальная задержка в мс.
<template>
<div>
<LazyMyComponent hydrate-on-idle />
</div>
</template>
hydrate-on-interaction
Гидрация после указанного события (click, mouseover и т.д.). По умолчанию: pointerenter, click, focus.
<template>
<div>
<LazyMyComponent hydrate-on-interaction="mouseover" />
</div>
</template>
hydrate-on-media-query
Гидрация при совпадении media query с окном.
<template>
<div>
<LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
</div>
</template>
hydrate-after
Гидрация через указанную задержку (мс).
<template>
<div>
<LazyMyComponent :hydrate-after="2000" />
</div>
</template>
hydrate-when
Гидрация по булевому условию.
<template>
<div>
<LazyMyComponent :hydrate-when="isReady" />
</div>
</template>
<script setup lang="ts">
const isReady = ref(false)
function myFunction () {
isReady.value = true
}
</script>
hydrate-never
Компонент не гидратируется.
<template>
<div>
<LazyMyComponent hydrate-never />
</div>
</template>
Событие гидрации
Компоненты с отложенной гидрацией эмитят @hydrated при гидрации.
<template>
<div>
<LazyMyComponent
hydrate-on-visible
@hydrated="onHydrate"
/>
</div>
</template>
<script setup lang="ts">
function onHydrate () {
console.log('Component has been hydrated!')
}
</script>
Ограничения и рекомендации
- Приоритет контенту в viewport: не откладывайте гидрацию для критичного контента над сгибом.
- Условный рендер: при
v-if="false"достаточно обычного ленивого компонента без отложенной гидрации. - Общее состояние: осторожнее с общим состоянием (v-model) между компонентами — обновление может запустить гидрацию у всех.
- Стратегии по назначению: каждая стратегия под свою задачу.
hydrate-when— для компонентов, которые не всегда нужно гидратировать;hydrate-after— когда можно подождать;hydrate-on-idle— когда можно ждать простоя браузера. - Не используйте
hydrate-neverдля интерактивных компонентов — компонент с взаимодействием пользователя должен гидратироваться.
Прямой импорт
Компоненты можно явно импортировать из #components, обходя автоимпорт.
<script setup lang="ts">
import { LazyMountainsList, NuxtLink } from '#components'
const show = ref(false)
</script>
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button v-if="!show" @click="show = true">Show List</button>
<NuxtLink to="/">Home</NuxtLink>
</div>
</template>
Свои директории
По умолчанию сканируется только ~/components. Дополнительные директории задаются в конфиге; вложенные пути указывайте раньше, сканирование идёт по порядку.
export default defineNuxtConfig({
components: [
{ path: '~/calendar-module/components' },
{ path: '~/user-module/components', pathPrefix: false },
{ path: '~/components/special-components', prefix: 'Special' },
'~/components',
],
})
Компоненты из npm
Для автоимпорта компонентов из npm-пакета используйте addComponent в локальном модуле.
import { addComponent, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup () {
addComponent({
name: 'MyAutoImportedComponent',
export: 'MyComponent',
filePath: 'my-npm-package',
})
},
})
<template>
<div>
<MyAutoImportedComponent />
</div>
</template>
Расширения файлов компонентов
По умолчанию компонентами считаются файлы с расширениями из extensions в nuxt.config. Ограничить можно через extensions в объявлении директории:
export default defineNuxtConfig({
components: [
{
path: '~/components',
extensions: ['.vue'],
},
],
})
Клиентские компоненты
Компонент только для клиента: суффикс .client в имени. Он рендерится только после монтирования.
| components/
--| Comments.client.vue
<template>
<div>
<Comments />
</div>
</template>
#components. Явный импорт по пути не делает компонент клиентским..client компонентов шаблон доступен в onMounted() только после await nextTick() в колбэке.Серверные компоненты
Серверные компоненты позволяют рендерить отдельные компоненты на сервере. Их можно использовать в Nuxt даже при статической генерации — для смешения динамики, серверного HTML и статической разметки.
Серверные компоненты бывают самостоятельными (islands) или в паре с клиентским компонентом.
Автономные серверные компоненты
Всегда рендерятся на сервере (islands). При обновлении пропов выполняется сетевой запрос и обновляется HTML на месте.
Экспериментальная возможность; включается в nuxt.config: experimental.componentIslands: true.
export default defineNuxtConfig({
experimental: {
componentIslands: true,
},
})
Компоненты только для сервера — суффикс .server, доступны по всему приложению.
-| components/
---| HighlightedMarkdown.server.vue
<template>
<div>
<HighlightedMarkdown markdown="# Headline" />
</div>
</template>
Под капотом используется <NuxtIsland> — ему передаются проп lazy и слот #fallback.
Клиентские компоненты внутри серверных
Нужно experimental.componentIslands.selectiveClient: true. Атрибут nuxt-client на дочернем компоненте подключает его на клиенте.
<template>
<div>
<HighlightedMarkdown markdown="# Headline" />
<Counter nuxt-client :count="5" />
</div>
</template>
experimental.componentIsland.selectiveClient: 'deep' и рендерятся на сервере, на клиенте не интерактивны.Контекст серверного компонента
При рендере server-only или island компонента <NuxtIsland> делает fetch; ответ — NuxtIslandResponse. Создаётся отдельное Vue-приложение и «островной» контекст; контекст островка изолирован от остального приложения. Плагины при рендере островка запускаются снова, если у них не задано env: { islands: false }.
В островном компоненте контекст доступен через nuxtApp.ssrContext.islandContext (формат может меняться, пока островки экспериментальны).
В паре с клиентским компонентом
Пара .server + .client — две «половинки» одного компонента для разной реализации на сервере и клиенте.
-| components/
---| Comments.client.vue
---| Comments.server.vue
<template>
<div>
<Comments />
</div>
</template>
Nuxt отрисует Comments.server на сервере, затем после монтирования подставит Comments.client.
Встроенные компоненты Nuxt
Nuxt поставляется с компонентами вроде <ClientOnly> и <DevOnly>. Подробнее в API.
Авторам библиотек
Регистрация директории компонентов с автоимпортом и tree-shaking — через addComponentsDir из @nuxt/kit в Nuxt-модуле.
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.ts
import { addComponentsDir, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup () {
const resolver = createResolver(import.meta.url)
addComponentsDir({
path: resolver.resolve('./components'),
prefix: 'awesome',
})
},
})
В проекте: modules: ['awesome-ui/nuxt'] в nuxt.config, затем в шаблоне <AwesomeButton>, <awesome-alert>. Компоненты подгружаются по мере использования и поддерживают HMR.