defineExpose, useSlots, useAttrs, useTemplateRef
↑ 4.1 Vue 3: основы, компоненты, Composition API · ← Предыдущая · → Следующая
Зачем это на собесе
Вспомогательные API
<script setup>: как получить слоты, атрибуты и открыть публичный интерфейс компонента.
Объяснение
<script setup lang="ts">
import { useSlots, useAttrs, useTemplateRef, getCurrentInstance, useId } from "vue";
const slots = useSlots(); // { default?, header?, ... }
const attrs = useAttrs(); // fallthrough-атрибуты
const hasHeader = !!slots.header;
const id = useId(); // стабильный уникальный id (SSR-safe) для label/aria
const el = useTemplateRef<HTMLDivElement>("root");
function focus() { el.value?.focus(); }
function validate(): boolean { /* ... */ return true; }
defineExpose({ focus, validate }); // публичный API для template ref родителя
</script>
<template>
<div ref="root" tabindex="-1"><slot /><slot name="header" v-if="hasHeader" /></div>
</template>Родитель:
<script setup lang="ts">
import type { ComponentExposed } from "vue-component-type-helpers";
const form = useTemplateRef<InstanceType<typeof ChildForm>>("form");
form.value?.validate();
</script>
<template><ChildForm ref="form" /></template>| API | Назначение |
|---|---|
defineExpose | что доступно родителю через ref |
useSlots, useAttrs | доступ к слотам и attrs в setup |
useTemplateRef | типобезопасный ref шаблона |
useId | идентификатор для доступности |
useModel | низкоуровневая версия defineModel |
getCurrentInstance | внутренний API (избегать) |
Идея: компонент — чёрный ящик, публичный интерфейс = props + emits + slots + defineExpose. Чем меньше expose, тем меньше связанности.
Нюансы и подводные камни
getCurrentInstance()— внутренний API, не для прикладного кода.exposeне реактивен для изменения снаружи; выставляйте методы, а не внутреннее состояние.useSlots()не реактивен на добавление слотов; используйте в шаблоне$slots.useIdне для ключейv-for.
Практика
- Экспортируйте
open()/close()из модального окна. - Свяжите
labelиinputчерезuseId. - Получите типы экспонируемых методов для родителя.
Вопросы с ответами
Зачем
defineExpose?В
<script setup>компонент закрыт; expose открывает только выбранные методы и данные родителю.
Как проверить наличие слота?
useSlots().nameили$slots.nameв шаблоне.