Атрибути, передані компоненту, але не оголошені як props чи emits, автоматично потрапляють на кореневий елемент компонента:
<!-- BaseButton.vue -->
<template>
<button class="btn"><slot /></button>
</template>
<BaseButton class="large" id="save" data-test="save-button" @focus="onFocus">Зберегти</BaseButton>
Результат:
<button class="btn large" id="save" data-test="save-button">Зберегти</button>
classіstyleоб'єднуються з тими, що вже є на кореневому елементі;- обробники подій (
@focus) теж прокидаються - як слухачі на кореневому елементі; - оголошені props і emits не прокидаються - їх компонент обробляє сам.
inheritAttrs: false + v-bind="$attrs" - коли атрибути мають потрапити не на корінь, а на внутрішній елемент. Класичний приклад - поле введення з обгорткою:
<!-- BaseInput.vue -->
<script setup>
defineOptions({ inheritAttrs: false });
defineProps(['label']);
</script>
<template>
<label class="field">
{{ label }}
<input v-bind="$attrs">
</label>
</template>
<BaseInput label="Email" type="email" placeholder="you@example.com" required />
type, placeholder, required потрапляють на <input>, а не на <label>.
Компонент з кількома кореневими елементами не має «кореня» - атрибути нікуди не прокидаються автоматично, і Vue виводить попередження. Треба явно вказати v-bind="$attrs" на потрібному елементі.
Доступ у скрипті: useAttrs() у <script setup>. Значення $attrs не реактивне в сенсі watch - для реакції на зміну краще оголосити prop.
Пастки:
- неоголошена подія прокидається як нативний слухач на корінь. Якщо компонент сам робить
emit('click'), аclickне оголошено вdefineEmits, обробник батька спрацює двічі - від нативного кліку й відemit. Тому події варто оголошувати завжди; classна компоненті зinheritAttrs: falseтеж не застосується до кореня автоматично - він входить у$attrs.