Динамические компоненты: component :is
↑ 4.1 Vue 3: основы, компоненты, Composition API · ← Предыдущая · → Следующая
Зачем это на собесе
Переключение компонентов по данным: табы, визарды, рендер по схеме.
Объяснение
<script setup lang="ts">
import { shallowRef, markRaw, defineAsyncComponent } from "vue";
import TextField from "./TextField.vue"; import SelectField from "./SelectField.vue";
const map = { text: TextField, select: SelectField } as const;
const current = shallowRef<Component>(TextField); // shallowRef/markRaw: компонент не должен быть глубоко реактивным
const fields = [{ type: "text", name: "a" }, { type: "select", name: "b" }];
</script>
<template>
<component :is="current" v-bind="props" @change="onChange" />
<!-- Рендер формы по схеме -->
<component v-for="f in fields" :key="f.name" :is="map[f.type]" v-bind="f" v-model="model[f.name]" />
<!-- Сохранение состояния переключаемых компонентов -->
<KeepAlive :include="['Tab1']" :max="5"><component :is="tabs[active]" /></KeepAlive>
<component :is="isLink ? 'a' : 'button'" :href="url" /> <!-- нативные теги -->
</template>:is принимает: строку (имя зарегистрированного компонента или тега), сам компонент (объект/функцию).
Особенности:
- Значение компонента храните в
shallowRef(илиmarkRaw), иначе Vue создаёт реактивный прокси над определением компонента (предупреждение и лишние расходы). - Переключение уничтожает предыдущий компонент; для сохранения состояния —
<KeepAlive>(activated/deactivated). - Атрибуты и слушатели прокидываются на текущий компонент.
- Для асинхронных вариантов —
defineAsyncComponent(см. Асинхронные компоненты и lazy loading).
Использования: табы, динамические формы (schema-driven UI), рендер виджетов дашборда, полиморфные компоненты (as-prop).
Нюансы и подводные камни
- Строка в
:isв SFC требует регистрации компонента (глобально илиresolveComponent). - Без
KeepAliveсостояние формы теряется при переключении. - Разные компоненты получают одни и те же props — лишние попадают в
$attrsи DOM. - Обращение к слотам динамического компонента — как обычно.
Практика
- Сделайте табы с
component :isиKeepAlive. - Отрендерите форму по JSON-схеме полей.
- Реализуйте полиморфный
Btn(a/button/RouterLink).
Вопросы с ответами
Что делает
<component :is>?Динамически рендерит компонент или тег по значению выражения.
Как сохранить состояние при переключении?
Обернуть в
<KeepAlive>.