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, предоставляемый Vue, или импортировать компонент напрямую из #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 к имени компонента. Это особенно полезно, если компонент нужен не всегда.
Используя префикс Lazy, вы можете отложить загрузку кода компонента до подходящего момента, что может быть полезно для оптимизации размера пакета JavaScript.
<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 поддерживает ленивую (отложенную) гидратацию и позволяет задать момент, когда компонент становится интерактивным.
Стратегии гидратации
Доступен набор встроенных стратегий. Для одного ленивого компонента можно использовать только одну стратегию.
hydrate-never приведёт к его гидратации).v-bind). Не работает с прямым импортом из #components.hydrate-on-visible
Гидратирует компонент, когда он попадает в область просмотра.
<template>
<div>
<LazyMyComponent hydrate-on-visible />
</div>
</template>
hydrateOnVisible.hydrate-on-idle
Гидратирует компонент, когда браузер простаивает. Подходит, если компонент нужен поскорее, но не должен блокировать критический путь рендеринга.
Можно передать число — максимальное время ожидания (мс).
<template>
<div>
<LazyMyComponent hydrate-on-idle />
</div>
</template>
hydrateOnIdle.hydrate-on-interaction
Гидратирует компонент после заданного взаимодействия (например, клик, наведение мыши).
<template>
<div>
<LazyMyComponent hydrate-on-interaction="mouseover" />
</div>
</template>
Если не передать событие или список событий, по умолчанию гидратация произойдёт на pointerenter, click и focus.
hydrateOnInteraction.hydrate-on-media-query
Гидратирует компонент, когда окно соответствует медиазапросу.
<template>
<div>
<LazyMyComponent hydrate-on-media-query="(max-width: 768px)" />
</div>
</template>
hydrateOnMediaQuery.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('Компонент гидратирован')
}
</script>
Ограничения и рекомендации
Отложенная гидратация может ускорить работу сайта, но важно применять её осмысленно:
- Контент в зоне видимости: не откладывайте гидратацию критичного контента «над сгибом»; стратегия лучше подходит для того, что не нужно сразу.
- Условный рендеринг: если у ленивого компонента стоит
v-if="false", отдельная отложенная гидратация часто не нужна — достаточно обычного ленивого компонента. - Общее состояние: учитывайте общий
v-modelмежду компонентами — обновление модели в одном может запустить гидратацию во всех связанных. - Назначение стратегий: у каждой своя ниша:
hydrate-when— когда компонент не всегда должен гидратироваться;hydrate-after— когда можно подождать фиксированное время;hydrate-on-idle— когда можно дождаться простоя браузера.
- Интерактивность и
hydrate-never: для компонентов, с которыми пользователь должен взаимодействовать, не используйтеhydrate-never.
Прямые импорты
Вы также можете явно импортировать компоненты из #components, если хотите, или если вам необходимо обойти функцию автоматического импорта Nuxt.
<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. Если вы хотите добавить другие директории или изменить способ сканирования компонентов в поддиректориях этой директории, вы можете добавить дополнительные директории в конфигурацию:
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',
],
})
Пакеты npm
Если вы хотите автоматически импортировать компоненты из пакета npm, вы можете использовать addComponent в локальном модуле для их регистрации.
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',
})
},
})
<template>
<div>
<!-- компонент использует указанное нами имя и импортируется автоматически -->
<MyAutoImportedComponent />
</div>
</template>
Расширения компонентов
По умолчанию, любой файл с расширением, указанным в ключе extensions nuxt.config.ts, рассматривается как компонент.
Если вам нужно ограничить расширения файлов, которые должны быть зарегистрированы как компоненты, вы можете использовать расширенную форму объявления директории компонентов и его ключ extensions:
export default defineNuxtConfig({
components: [
{
path: '~/components',
extensions: ['.vue'], },
],
})
Клиентские компоненты
Если компонент предназначен для рендера только на стороне клиента, вы можете добавить суффикс .client к своему компоненту.
| components/
--| Comments.client.vue
<template>
<div>
<!-- этот компонент будет отображаться только на стороне клиента -->
<Comments />
</div>
</template>
#components. Явный импорт таких компонентов по их реальному пути не преобразует их в клиентские компоненты..client рендерятся только после монтирования. Чтобы получить доступ к отрисованному шаблону с помощью onMounted(), добавьте await nextTick() в коллбэк хука onMounted().Серверные компоненты
Серверные компоненты позволяют серверный рендеринг отдельных компонентов в клиентских приложениях. Серверные компоненты можно использовать в Nuxt, даже если вы создаете статический сайт. Это позволяет создавать сложные сайты, которые смешивают динамические компоненты, HTML, рендеринг которого осуществляется сервером, и даже статические фрагменты разметки.
Серверные компоненты могут использоваться как по отдельности, так и в паре с клиентским компонентом.
Автономные серверные компоненты
Отдельные серверные компоненты, которые всегда будут отображаться на сервере, также известны как island-компоненты.
Когда их свойства обновляются, это приводит к сетевому запросу, который обновляет отображаемый HTML.
Серверные компоненты пока экспериментальны; чтобы их использовать, включите в nuxt.config опцию «островных компонентов» (component islands):
export default defineNuxtConfig({
experimental: {
componentIslands: true,
},
})
Теперь вы можете регистрировать серверные компоненты с суффиксом .server и автоматически использовать их в любом месте вашего приложения.
-| components/
---| HighlightedMarkdown.server.vue
<template>
<div>
<!--
это будет автоматически отрендерено на сервере, то есть ваши библиотеки парсинга и подсветки markdown не будут включены в ваш клиентский бандл.
-->
<HighlightedMarkdown markdown="# Заголовок" />
</div>
</template>
Компоненты, предназначенные только для сервера, используют <NuxtIsland> под капотом, что означает, что свойство lazy и слот #fallback передаются им.
Клиентские компоненты внутри серверных компонентов
experimental.componentIslands.selectiveClient.Вы можете частично гидратировать компонент, установив атрибут nuxt-client для компонента, который вы хотите загрузить на стороне клиента.
<template>
<div>
<HighlightedMarkdown markdown="# Заголовок" />
<!-- Счётчик будет загружен и гидратирован на клиенте -->
<Counter
nuxt-client
:count="5"
/>
</div>
</template>
experimental.componentIsland.selectiveClient, установленным на 'deep', и поскольку они рендерятся на стороне сервера, они не являются интерактивными на стороне клиента.Контекст серверного компонента
При рендеринге серверного или island-компонента <NuxtIsland> выполняет запрос; в ответ приходит NuxtIslandResponse. На сервере это внутренний запрос; в браузере такой запрос виден на вкладке «Сеть», если остров рендерится на клиенте.
Отсюда следует:
- На сервере для формирования
NuxtIslandResponseсоздаётся отдельное приложение Vue. - Во время рендеринга компонента создаётся свой контекст острова (
island context). - К контексту острова нельзя обратиться из остального приложения, и из острова недоступен контекст приложения: серверный и island-компонент изолированы от остальной части приложения.
- Плагины снова запускаются при рендере острова, если в объектном синтаксисе плагина не задано
env: { islands: false }.
Внутри island-компонента контекст доступен через nuxtApp.ssrContext.islandContext. Пока острова экспериментальны, формат этого контекста может меняться.
<div> с display: contents;.В паре с клиентским компонентом
В этом случае компоненты .server + .client представляют собой две «половинки» компонента и могут использоваться в расширенных вариантах использования для отдельных реализаций компонента на стороне сервера и клиента.
-| components/
---| Comments.client.vue
---| Comments.server.vue
<template>
<div>
<!-- этот компонент отрендерит Comments.server на сервере, а затем Comments.client после монтирования в браузере -->
<Comments />
</div>
</template>
Встроенные компоненты Nuxt
Nuxt предоставляет ряд компонентов, включая <ClientOnly> и <DevOnly>. Подробнее о них можно прочитать в документации API.
Авторам библиотек
Создавать библиотеки компонентов Vue с автоматическим отсечением неиспользуемого кода (tree-shaking) и регистрацией компонентов очень просто. ✨
Для регистрации директории компонентов в Nuxt-модуле используйте метод addComponentsDir из @nuxt/kit.
Представьте себе такую структуру директорий:
-| node_modules/
---| awesome-ui/
-----| components/
-------| Alert.vue
-------| Button.vue
-----| nuxt.ts
-| pages/
---| index.vue
-| nuxt.config.ts
Затем в awesome-ui/nuxt.ts вы можете использовать хук addComponentsDir:
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:
export default defineNuxtConfig({
modules: ['awesome-ui/nuxt'],
})
... и напрямую использовать компоненты модуля (с префиксом awesome-) в нашем pages/index.vue:
<template>
<div>
Моя <AwesomeButton>UI кнопка</AwesomeButton>!
<awesome-alert>Это алерт!</awesome-alert>
</div>
</template>
Это автоматически импортирует компоненты только в том случае, если они используются, а также поддерживает HMR при обновлении компонентов в node_modules/awesome-ui/components/.