defineExpose, useSlots, useAttrs, useTemplateRef

↑ 4.1 Vue 3: основы, компоненты, Composition API · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Вспомогательные 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.

Практика

  1. Экспортируйте open()/close() из модального окна.
  2. Свяжите label и input через useId.
  3. Получите типы экспонируемых методов для родителя.

Вопросы с ответами

Связанные темы