# components

> Каталог components/ — место для Vue-компонентов приложения.

Nuxt автоматически импортирует компоненты из этого каталога и компоненты, которые регистрируют подключённые модули.

```bash [Directory Structure]
-| components/
---| AppHeader.vue
---| AppFooter.vue
```

```html [app.vue]
<template>
  <div>
    <AppHeader />
    <NuxtPage />
    <AppFooter />
  </div>
</template>
```

## Имена компонентов

Если у вас есть компонент во вложенных директориях, например:

```bash [Directory Structure]
-| components/
---| base/
-----| foo/
-------| Button.vue
```

... тогда имя компонента будет основано на его пути в директориях и имени файла, при этом повторяющиеся сегменты будут удалены. Таким образом, имя компонента будет:

```html
<BaseFooButton />
```

<note>

Для ясности лучше, чтобы имя файла совпадало с именем компонента. В примере выше можно переименовать `Button.vue` в `BaseFooButton.vue`.

</note>

Если вы хотите автоматически импортировать компоненты только на основе их имени, а не пути, вам необходимо установить для параметра `pathPrefix` значение `false`, используя расширенную форму объекта конфигурации:

```ts [nuxt.config.ts]twoslash
export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
      pathPrefix: false, // [!code ++]
    },
  ],
})
```

При этом компоненты будут регистрироваться с использованием той же стратегии, что и в Nuxt 2. Например, `~/components/Some/MyComponent.vue` будет использоваться как `<MyComponent>` но не `<SomeMyComponent>`.

## Динамические компоненты

Если вы хотите использовать синтаксис Vue `<component :is="someComputedComponent">`, вам необходимо использовать помощник `resolveComponent`, предоставляемый Vue, или импортировать компонент напрямую из `#components` и передать его в свойство `is`.

Например:

```vue [pages/index.vue]
<script setup lang="ts">
import { SomeComponent } from '#components'

const MyButton = resolveComponent('MyButton')
</script>

<template>
  <component :is="clickable ? MyButton : 'div'" />
  <component :is="SomeComponent" />
</template>
```

<important>

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

</important>

<video-accordion title="Короткое видео Дэниела Ро о resolveComponent()" video-id="4kq8E5IUM2U">



</video-accordion>

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

```diff
export default defineNuxtConfig({
    components: {
+     global: true,
+     dirs: ['~/components']
    },
  })
```

Вы также можете выборочно зарегистрировать некоторые компоненты глобально, поместив их в директорию `~/components/global` или используя суффикс `.global.vue` в имени файла. Как отмечено выше, каждый глобальный компонент отображается в отдельном чанке, поэтому будьте осторожны и не злоупотребляйте этой функцией.

<note>

Параметр `global` также можно задать для каждой директории компонента.

</note>

## Динамические импорты

Для динамического импорта компонента (также известного как ленивая загрузка компонента) все, что вам нужно сделать, это добавить префикс `Lazy` к имени компонента. Это особенно полезно, если компонент нужен не всегда.

Используя префикс `Lazy`, вы можете отложить загрузку кода компонента до подходящего момента, что может быть полезно для оптимизации размера пакета JavaScript.

```vue [pages/index.vue]
<script setup lang="ts">
const show = ref(false)
</script>

<template>
  <div>
    <h1>Горы</h1>
    <LazyMountainsList v-if="show" />
    <button
      v-if="!show"
      @click="show = true"
    >
      Показать список
    </button>
  </div>
</template>
```

## Отложенная (ленивая) гидратация

Префикс `Lazy` у компонентов помогает контролировать размер чанков, но не всегда улучшает производительность в рантайме: код всё равно подгружается сразу, если компонент не скрыт условием. На страницах с большим количеством контента и компонентов далеко не всем нужна интерактивность сразу после загрузки; если всё грузить сразу, это бьёт по производительности.

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

Nuxt поддерживает ленивую (отложенную) гидратацию и позволяет задать момент, когда компонент становится интерактивным.

### Стратегии гидратации

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

<note>

Любое изменение пропа у лениво гидратируемого компонента немедленно запускает гидратацию (например, смена пропа у компонента с `hydrate-never` приведёт к его гидратации).

</note>

<warning>

Встроенная ленивая гидратация Nuxt работает только в однофайловых компонентах (SFC), проп нужно задать в шаблоне явно (нельзя разворачивать объект пропов через `v-bind`). Не работает с прямым импортом из `#components`.

</warning>

#### `hydrate-on-visible`

Гидратирует компонент, когда он попадает в область просмотра.

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent hydrate-on-visible />
  </div>
</template>
```

<read-more to="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver" title="Параметры IntersectionObserver">

Подробнее о настройках для `hydrate-on-visible`.

</read-more>

<note>

Под капотом используется встроенная в Vue стратегия [`hydrateOnVisible`](https://vuejs.org/guide/components/async#hydrate-on-visible).

</note>

#### `hydrate-on-idle`

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

Можно передать число — максимальное время ожидания (мс).

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent hydrate-on-idle />
  </div>
</template>
```

<note>

Под капотом используется встроенная в Vue стратегия [`hydrateOnIdle`](https://vuejs.org/guide/components/async#hydrate-on-idle).

</note>

#### `hydrate-on-interaction`

Гидратирует компонент после заданного взаимодействия (например, клик, наведение мыши).

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent hydrate-on-interaction="mouseover" />
  </div>
</template>
```

Если не передать событие или список событий, по умолчанию гидратация произойдёт на `pointerenter`, `click` и `focus`.

<note>

Под капотом используется встроенная в Vue стратегия [`hydrateOnInteraction`](https://vuejs.org/guide/components/async#hydrate-on-interaction).

</note>

#### `hydrate-on-media-query`

Гидратирует компонент, когда окно соответствует медиазапросу.

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
  </div>
</template>
```

<note>

Под капотом используется встроенная в Vue стратегия [`hydrateOnMediaQuery`](https://vuejs.org/guide/components/async#hydrate-on-media-query).

</note>

#### `hydrate-after`

Гидратирует компонент после указанной задержки (в миллисекундах).

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent :hydrate-after="2000" />
  </div>
</template>
```

#### `hydrate-when`

Гидратирует компонент при истинном значении булева условия.

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent :hydrate-when="isReady" />
  </div>
</template>

<script setup lang="ts">
const isReady = ref(false)
function myFunction () {
  // запуск пользовательской стратегии гидратации…
  isReady.value = true
}
</script>
```

#### `hydrate-never`

Компонент никогда не гидратируется.

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent hydrate-never />
  </div>
</template>
```

### События гидратации

Компоненты с отложенной гидратацией испускают событие `@hydrated`, когда гидратация завершена.

```vue [pages/index.vue]
<template>
  <div>
    <LazyMyComponent
      hydrate-on-visible
      @hydrated="onHydrate"
    />
  </div>
</template>

<script setup lang="ts">
function onHydrate () {
  console.log('Компонент гидратирован')
}
</script>
```

### Ограничения и рекомендации

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

1. **Контент в зоне видимости:** не откладывайте гидратацию критичного контента «над сгибом»; стратегия лучше подходит для того, что не нужно сразу.
2. **Условный рендеринг:** если у ленивого компонента стоит `v-if="false"`, отдельная отложенная гидратация часто не нужна — достаточно обычного ленивого компонента.
3. **Общее состояние:** учитывайте общий `v-model` между компонентами — обновление модели в одном может запустить гидратацию во всех связанных.
4. **Назначение стратегий:** у каждой своя ниша:
  - `hydrate-when` — когда компонент не всегда должен гидратироваться;
  - `hydrate-after` — когда можно подождать фиксированное время;
  - `hydrate-on-idle` — когда можно дождаться простоя браузера.
5. **Интерактивность и hydrate-never:** для компонентов, с которыми пользователь должен взаимодействовать, не используйте `hydrate-never`.

## Прямые импорты

Вы также можете явно импортировать компоненты из `#components`, если хотите, или если вам необходимо обойти функцию автоматического импорта Nuxt.

```vue [pages/index.vue]
<script setup lang="ts">
import { LazyMountainsList, NuxtLink } from '#components'

const show = ref(false)
</script>

<template>
  <div>
    <h1>Горы</h1>
    <LazyMountainsList v-if="show" />
    <button
      v-if="!show"
      @click="show = true"
    >
      Показать список
    </button>
    <NuxtLink to="/">Главная</NuxtLink>
  </div>
</template>
```

## Пользовательские директории

По умолчанию сканируется только директория `~/components`. Если вы хотите добавить другие директории или изменить способ сканирования компонентов в поддиректориях этой директории, вы можете добавить дополнительные директории в конфигурацию:

```ts [nuxt.config.ts]twoslash
export default defineNuxtConfig({
  components: [
    // ~/calendar-module/components/event/Update.vue => <EventUpdate />
    { path: '~/calendar-module/components' },

    // ~/user-module/components/account/UserDeleteDialog.vue => <UserDeleteDialog />
    { path: '~/user-module/components', pathPrefix: false },

    // ~/components/special-components/Btn.vue => <SpecialBtn />
    { path: '~/components/special-components', prefix: 'Special' },

    // Важно: переопределения для подпапок `~/components` указывайте последними.
    //
    // ~/components/Btn.vue => <Btn />
    // ~/components/base/Btn.vue => <BaseBtn />
    '~/components',
  ],
})
```

<note>

Любые вложенные директории должны быть добавлены первыми, так как они сканируются по порядку.

</note>

## Пакеты npm

Если вы хотите автоматически импортировать компоненты из пакета npm, вы можете использовать [`addComponent`](/docs/3.x/api/kit/components#addcomponent) в [локальном модуле](/docs/3.x/directory-structure/modules) для их регистрации.

<code-group>

```ts [~/modules/register-component.ts]twoslash
import { addComponent, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup () {
    // import { MyComponent as MyAutoImportedComponent } from 'my-npm-package'
    addComponent({
      name: 'MyAutoImportedComponent',
      export: 'MyComponent',
      filePath: 'my-npm-package',
    })
  },
})
```

```vue [app.vue]
<template>
  <div>
    <!--  компонент использует указанное нами имя и импортируется автоматически  -->
    <MyAutoImportedComponent />
  </div>
</template>
```

</code-group>

## Расширения компонентов

По умолчанию, любой файл с расширением, указанным в [ключе `extensions` `nuxt.config.ts`](/docs/3.x/api/nuxt-config#extensions), рассматривается как компонент.
Если вам нужно ограничить расширения файлов, которые должны быть зарегистрированы как компоненты, вы можете использовать расширенную форму объявления директории компонентов и его ключ `extensions`:

```ts [nuxt.config.ts]twoslash
export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
      extensions: ['.vue'], // [!code ++]
    },
  ],
})
```

## Клиентские компоненты

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

```bash [Directory Structure]
| components/
--| Comments.client.vue
```

```vue [pages/example.vue]
<template>
  <div>
    <!-- этот компонент будет отображаться только на стороне клиента -->
    <Comments />
  </div>
</template>
```

<note>

Эта функция работает только с автоимпортами Nuxt и импортами `#components`. Явный импорт таких компонентов по их реальному пути не преобразует их в клиентские компоненты.

</note>

<important>

Компоненты `.client` рендерятся только после монтирования. Чтобы получить доступ к отрисованному шаблону с помощью `onMounted()`, добавьте `await nextTick()` в коллбэк хука `onMounted()`.

</important>

<read-more to="/docs/3.x/api/components/client-only">

Аналогичного результата можно добиться и с помощью компонента `<ClientOnly>`.

</read-more>

## Серверные компоненты

Серверные компоненты позволяют серверный рендеринг отдельных компонентов в клиентских приложениях. Серверные компоненты можно использовать в Nuxt, даже если вы создаете статический сайт. Это позволяет создавать сложные сайты, которые смешивают динамические компоненты, HTML, рендеринг которого осуществляется сервером, и даже статические фрагменты разметки.

Серверные компоненты могут использоваться как по отдельности, так и в паре с [клиентским компонентом](#%D0%B2-%D0%BF%D0%B0%D1%80%D0%B5-%D1%81-%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82%D1%81%D0%BA%D0%B8%D0%BC-%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82%D0%BE%D0%BC).

<video-accordion title="Видео о серверных компонентах Nuxt (канал LearnVue)" video-id="u1yyXe86xJM">



</video-accordion>

<tip icon="i-lucide-newspaper" target="_blank" to="https://roe.dev/blog/nuxt-server-components">

Прочитайте руководство Дэниела Ро по серверным компонентам Nuxt.

</tip>

### Автономные серверные компоненты

Отдельные серверные компоненты, которые всегда будут отображаться на сервере, также известны как island-компоненты.

Когда их свойства обновляются, это приводит к сетевому запросу, который обновляет отображаемый HTML.

Серверные компоненты пока экспериментальны; чтобы их использовать, включите в `nuxt.config` опцию «островных компонентов» (`component islands`):

```ts [nuxt.config.ts]twoslash
export default defineNuxtConfig({
  experimental: {
    componentIslands: true,
  },
})
```

Теперь вы можете регистрировать серверные компоненты с суффиксом `.server` и автоматически использовать их в любом месте вашего приложения.

```bash [Directory Structure]
-| components/
---| HighlightedMarkdown.server.vue
```

```vue [pages/example.vue]
<template>
  <div>
    <!--
      это будет автоматически отрендерено на сервере, то есть ваши библиотеки парсинга и подсветки markdown не будут включены в ваш клиентский бандл.
     -->
    <HighlightedMarkdown markdown="# Заголовок" />
  </div>
</template>
```

Компоненты, предназначенные только для сервера, используют [`<NuxtIsland>`](/docs/3.x/api/components/nuxt-island) под капотом, что означает, что свойство `lazy` и слот `#fallback` передаются им.

<warning>

Серверные компоненты (и islands) должны иметь один корневой элемент. (Комментарии HTML также считаются элементами.)

</warning>

<warning>

Пропсы серверных компонентов передаются через параметры строки запроса URL и ограничены её длиной; не передавайте через пропсы слишком большие объёмы данных.

</warning>

<warning>

Будьте осторожны при вложении островов друг в друга: каждый остров добавляет накладные расходы.

</warning>

<warning>

Большинство функций для серверных компонентов и island-компонентов, таких как слоты и клиентские компоненты, доступны только для однофайловых компонентов.

</warning>

#### Клиентские компоненты внутри серверных компонентов

<note>

Для работы этой функции в вашей конфигурации должен быть указан `experimental.componentIslands.selectiveClient`.

</note>

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

```vue [components/ServerWithClient.vue]
<template>
  <div>
    <HighlightedMarkdown markdown="# Заголовок" />
    <!-- Счётчик будет загружен и гидратирован на клиенте -->
    <Counter
      nuxt-client
      :count="5"
    />
  </div>
</template>
```

<note>

Это работает только в серверном компоненте. Слоты для клиентских компонентов работают только с `experimental.componentIsland.selectiveClient`, установленным на `'deep'`, и поскольку они рендерятся на стороне сервера, они не являются интерактивными на стороне клиента.

</note>

#### Контекст серверного компонента

При рендеринге серверного или island-компонента `<NuxtIsland>` выполняет запрос; в ответ приходит `NuxtIslandResponse`. На сервере это внутренний запрос; в браузере такой запрос виден на вкладке «Сеть», если остров рендерится на клиенте.

Отсюда следует:

- На сервере для формирования `NuxtIslandResponse` создаётся отдельное приложение Vue.
- Во время рендеринга компонента создаётся свой контекст острова (`island context`).
- К контексту острова нельзя обратиться из остального приложения, и из острова недоступен контекст приложения: серверный и island-компонент *изолированы* от остальной части приложения.
- Плагины снова запускаются при рендере острова, если в объектном синтаксисе плагина не задано `env: { islands: false }`.

<important>

Middleware маршрута не выполняется при рендеринге island-компонентов. Middleware относится к маршрутизации страниц, а не к рендерингу компонентов.

</important>

Внутри island-компонента контекст доступен через `nuxtApp.ssrContext.islandContext`. Пока острова экспериментальны, формат этого контекста может меняться.

<note>

Слоты могут быть интерактивными и заключаются в `<div>` с `display: contents;`.

</note>

### В паре с клиентским компонентом

В этом случае компоненты `.server` + `.client` представляют собой две «половинки» компонента и могут использоваться в расширенных вариантах использования для отдельных реализаций компонента на стороне сервера и клиента.

```bash [Directory Structure]
-| components/
---| Comments.client.vue
---| Comments.server.vue
```

```vue [pages/example.vue]
<template>
  <div>
    <!-- этот компонент отрендерит Comments.server на сервере, а затем Comments.client после монтирования в браузере -->
    <Comments />
  </div>
</template>
```

## Встроенные компоненты Nuxt

Nuxt предоставляет ряд компонентов, включая `<ClientOnly>` и `<DevOnly>`. Подробнее о них можно прочитать в документации API.

<read-more to="/docs/3.x/api">

Полный перечень встроенных компонентов — в разделе **API Nuxt**.

</read-more>

## Авторам библиотек

Создавать библиотеки компонентов Vue с автоматическим отсечением неиспользуемого кода (tree-shaking) и регистрацией компонентов очень просто. ✨

Для регистрации директории компонентов в Nuxt-модуле используйте метод [`addComponentsDir`](/docs/3.x/api/kit/components#addcomponentsdir) из `@nuxt/kit`.

Представьте себе такую структуру директорий:

```bash [Directory Structure]
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.ts
-| pages/
---| index.vue
-| nuxt.config.ts
```

Затем в `awesome-ui/nuxt.ts` вы можете использовать хук `addComponentsDir`:

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

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

    // Добавьте директорию ./components в список
    addComponentsDir({
      path: resolver.resolve('./components'),
      prefix: 'awesome',
    })
  },
})
```

Готово. Теперь в проекте можно подключить UI-библиотеку как модуль Nuxt в `nuxt.config`:

```ts [nuxt.config.ts]twoslash
export default defineNuxtConfig({
  modules: ['awesome-ui/nuxt'],
})
```

... и напрямую использовать компоненты модуля (с префиксом `awesome-`) в нашем `pages/index.vue`:

```vue
<template>
  <div>
    Моя <AwesomeButton>UI кнопка</AwesomeButton>!
    <awesome-alert>Это алерт!</awesome-alert>
  </div>
</template>
```

Это автоматически импортирует компоненты только в том случае, если они используются, а также поддерживает HMR при обновлении компонентов в `node_modules/awesome-ui/components/`.

<link-example to="/docs/3.x/examples/features/auto-imports">



</link-example>
