Олег Марков
Работа с динамическими компонентами и данными в Vue
Введение
Опыт построения современных веб-интерфейсов часто требует гибкого управления отображаемыми компонентами в зависимости от пользовательских действий или полученных данных. В экосистеме Vue существует несколько эффективных инструментов, которые позволяют динамически менять компоненты и манипулировать связанными с ними данными.
В этой статье вы разберете, как использовать динамические компоненты (component), как связывать с ними данные, когда потребуется управлять их состоянием или реализовать переиспользуемые шаблоны. Также я покажу, как организовывать их жизненный цикл, прокидывать props, работать с передачей событий и использовать слоты в динамических связках.
Что такое динамические компоненты в Vue
Динамические компоненты позволяют вам отображать во вьюхе один из множества заранее определенных компонентов в зависимости от текущего состояния данных или пользовательских действий. Это крайне удобно, если необходимо, чтобы одни и те же данные визуализировались разными способами — например, при построении табов, виджетов, редакторов или систем вывода уведомлений.
Общий подход
В основе работы с динамическими компонентами лежит специальный элемент <component>, которому через атрибут :is передается либо строковое имя компонента, либо сам объект компонента.
Пример:
<template>
<!-- В зависимости от currentView, здесь отображается один из компонентов -->
<component :is="currentView"></component>
</template>
<script>
import MyList from './MyList.vue'
import MyGrid from './MyGrid.vue'
export default {
data() {
return {
// Выбор компонента может происходить динамически
currentView: 'MyList'
}
},
components: {
MyList,
MyGrid
}
}
</script>
В этом примере, если currentView содержит 'MyList', будет отрисован компонент MyList. Если значение поменять на 'MyGrid', на этой же позиции появится MyGrid. Такой код идеально подходит для построения динамических секций интерфейса.
Изучение динамических компонентов открывает большие возможности в Vue. Но для создания гибких и поддерживаемых приложений необходимо понимать, как правильно организовать компоненты, управлять состоянием и наладить взаимодействие между ними. Если вы хотите получить всесторонние знания и навыки в разработке Vue-приложений, приходите на наш большой курс Vue.js 3, Vue Router и Pinia. На курсе 173 уроков и 21 упражнение, AI-тренажеры для безлимитной практики с кодом и задачами 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.
Передача данных в динамические компоненты
Использование props
Динамический компонент, как и обычный, может принимать props. Важно помнить: если у вас есть разные компоненты с разными набором props, стоит удостовериться, что передаваемые props есть у всех компонентов, иначе может быть предупреждение, либо не все props попадут туда, куда ожидали.
Вот пример — передаем данные в динамический компонент:
<template>
<!-- Передаем user как prop каждому динамическому компоненту -->
<component :is="selectedComponent" :user="userData"></component>
</template>
<script>
import UserCard from './UserCard.vue'
import UserProfile from './UserProfile.vue'
export default {
data() {
return {
selectedComponent: 'UserCard',
userData: { name: 'Anna', email: 'anna@mail.com' }
}
},
components: {
UserCard,
UserProfile
}
}
</script>
В этом случае оба компонента UserCard и UserProfile должны принимать проп user. Если нужен более гибкий способ — можно использовать v-bind="{...}" для передачи всех свойств сразу.
Передача всех props через v-bind
Если вы хотите передать сразу несколько props, можно использовать v-bind с объектом. Это удобно для унифицированных структур данных:
<component :is="active" v-bind="componentProps"></component>
<!-- компонентProps = { title: 'hello', subtitle: 'subtitle text', active: true } -->
Этот способ быстро раскрывает весь объект в props динамического компонента.
Смена компонента по событию
Вы часто встретите сценарии, когда требуется поменять активный компонент по клику или другому событию пользователя. Для этого достаточно просто обновить значение переменной, передаваемой в :is:
<template>
<button @click="toggleView">Поменять вид</button>
<component :is="current"></component>
</template>
<script>
import ViewA from './ViewA.vue'
import ViewB from './ViewB.vue'
export default {
data() {
return {
current: 'ViewA'
}
},
components: { ViewA, ViewB },
methods: {
toggleView() {
// Меняет отображаемый компонент
this.current = this.current === 'ViewA' ? 'ViewB' : 'ViewA'
}
}
}
</script>
Сразу при клике отрендерится другой компонент — быстро, без перерисовки всей страницы.
Уникальность состояния и key
Если у вас используются динамические компоненты, и при переключении нужно сохранять их состояние (например, введенные значения в форме), Vue позволяет это сделать через компонент <keep-alive>. Но если нужно сбрасывать внутреннее состояние компонентов при каждом переключении, стоит использовать уникальный key:
<component :is="currentView" :key="currentView"></component>
Здесь каждый раз при смене значения currentView компонент будет пересоздаваться заново и внутреннее состояние сбросится.
Сохранение состояния с <keep-alive>
<keep-alive> сохраняет внутреннее состояние динамического компонента — это полезно для форм и тяжелых компонентов. Работает это очень просто:
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
Допустим, если вы вводите текст в форме в одном компоненте, переключаетесь, а затем возвращаетесь — все введенные данные останутся.
Настройки keep-alive
Вы можете ограничить какие именно компоненты держать в alive через include/exclude, используя строку с именами компонентов:
<keep-alive include="MyList,MyGrid">
<component :is="currentView"></component>
</keep-alive>
Динамические слоты
Иногда требуется прокинуть индивидуальный контент в разный компонент. В Vue 3 доступны динамические слоты (Dynamic Slots) через объект:
<component
:is="dynamic"
v-bind="componentProps"
v-slot="slotProps">
{{ slotProps.someData }}
</component>
Если хочется более контролируемо управлять содержимым для разных компонентов, рекомендую явно задавать имена слотов:
<component :is="tabComp">
<template #header>
<!-- будет использован слот header в любом из активных компонентов -->
<h1>Заголовок для текущего компонента</h1>
</template>
</component>
Динамические компоненты как шаблонные конструкторы
Если у вас есть однотипные шаблоны с разной логикой (например, Alert Success, Alert Error, Alert Info), используйте массив к компоненту:
<component
v-for="type in alertTypes"
:key="type"
:is="type"
:message="alertText[type]">
</component>
Здесь для каждого типа алерта будет свой компонент, и каждому из них можно прокинуть индивидуальные props.
Передача событий между динамическими компонентами
Вся работа с событиями (@event) абсолютно идентична работе с обычным компонентом. Например:
<component :is="widget" @save="handleSave"></component>
Здесь если внутренний компонент вызовет $emit('save'), этот обработчик сработает.
Динамические вставки с асинхронными компонентами
В Vue можно использовать динамические компоненты, которые еще не загружены — удобно для ленивой подгрузки:
<template>
<component :is="asyncComponent"></component>
</template>
<script>
export default {
data() {
return {
asyncComponent: () => import('./BigComponent.vue')
}
}
}
</script>
Теперь компонент подгрузится только тогда, когда будет показан во вьюхе, это ускоряет старт загрузки страницы.
Управление списками динамических компонентов
Если у вас есть коллекция однотипных динамических компонентов, используйте их в цикле. Пример — список карточек разных типов:
<template>
<component
v-for="item in widgets"
:key="item.id"
:is="item.type"
v-bind="item.props">
</component>
</template>
<script>
import WidgetA from './WidgetA.vue'
import WidgetB from './WidgetB.vue'
export default {
data() {
return {
widgets: [
{ id: 1, type: 'WidgetA', props: { value: 10 }},
{ id: 2, type: 'WidgetB', props: { color: 'red' }}
]
}
},
components: { WidgetA, WidgetB }
}
</script>
Динамическое подключение через ключ type подставляет компонент по его имени. Все необходимые props прокинутся через v-bind.
Типичные проблемы и нюансы
- Значение атрибута
:isобязательно должно быть строкой (именем зарегистрированного компонента) или объектом — иначе будет ошибка. - Все компоненты, которые вы хотите использовать динамически, должны быть заранее импортированы и зарегистрированы.
- Если для разных компонентов нужны разные props, стоит использовать подход с общим родительским компонентом-оберткой.
Динамические компоненты и реактивность
Важный нюанс — если вы меняете props у динамического компонента, данные обновятся моментально, как и у обычных компонентов. Никаких специальных “магий” по синхронизации делать не требуется.
Однако если вы работаете с массивом динамических компонентов, обязательно следите за корректной установкой key во v-for, чтобы избежать "перемешивания" состояний между компонентами.
Работа с render-функциями для особо гибких случаев
В редких случаях, если недостаточно возможностей шаблонов, используйте render-функции или JSX (только при наличии соответствующего билда). Такой подход позволяет динамично подставлять компоненты по условиям и программно формировать вложенность.
Пример на обычной render-функции:
render(h) {
// Программно выбираем компонент
return h(this.current, {
props: this.componentProps
})
}
В большинстве случаев хватает декларативных шаблонов — программные рендеры нужны редко, например при написании библиотек.
Интеграция с Vue Router
С помощью <router-view> реализуются layout-подобные связи, где содержимое меняется динамически по маршруту. По сути, <router-view> — тоже динамический компонент.
Вы даже можете обернуть <router-view> в <keep-alive>, чтобы сохранять состояние вложенных страниц.
Заключение
Вы научились не только подключать и управлять такими компонентами, но и передавать им данные, обрабатывать события, работать с состоянием — и все это с сохранением реактивности фреймворка.
Этот подход пригодится практически в любом “живом” проекте: от простых вкладок до сложных интерфейсов редактирования, работающих с большим количеством уникальных или повторяющихся блоков.
Динамические компоненты — это мощный инструмент, но он требует глубокого понимания архитектуры приложения. Чтобы создавать по-настоящему профессиональные решения, нужно уверенно владеть маршрутизацией, управлением состоянием и другими важными аспектами. На нашем курсе Vue.js 3, Vue Router и Pinia вы получите все необходимые знания и навыки. В первых 3 модулях уже доступно бесплатное содержание — начните погружаться в мир Vue прямо сегодня.
Частозадаваемые технические вопросы по теме статьи и ответы на них
Как прокинуть уникальные props разным динамическим компонентам?
Если у разных компонентов props с разными именами, используйте computed-поле, которое строит объект props по имени компонента:
computed: {
currentProps() {
if (this.currentView === 'CompA') return { foo: 1 }
if (this.currentView === 'CompB') return { bar: 2 }
return {}
}
}
И вызывайте: <component :is="currentView" v-bind="currentProps"></component>
Как корректно динамически загружать компоненты на серверном рендеринге (SSR)?
Используйте async import с дефолтным экспортом:
const AsyncComp = () => import('./AsyncComp.vue')
В серверном режиме загрузка немного отличается: компонент загружается до рендера страницы.
Почему не обновляются props в динамическом компоненте при смене данных?
Проверьте, используется ли корректный key:
<component :is="comp" :key="compKey" :prop="val" />
Если key статичен, компонент не пересоздается и не всегда видит новые props.
Как слушать собственные события от вложенных динамических компонентов?
Всё как обычно: <component :is="current" @event="handler"></component>. Не забудьте, что вложенный компонент должен явно вызывать $emit('event').
Можно ли динамически выбирать компонент только на этапе рендера и не хранить его в data?
Да, просто использовать выражение:
<component :is="someCondition ? 'CompA' : 'CompB'"></component>
Это сокращает количество переменных в data, но подойдет не для всех кейсов, где быстрая смена нужна по клику.
Постройте личный план изучения Vue до уровня Middle — бесплатно!
Vue — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Vue
Лучшие курсы по теме

Vue 3 и Pinia
Антон Ларичев
TypeScript с нуля
Антон Ларичев