Sygnaly telemetryczne, ktore nie sa zdefiniowane jako props, automatycznie "przenikaja" do glownego elementu komponentu - jak dane z czujnikow do centralnego procesora NOVA LAB. Ten mechanizm nazywa sie fallthrough attributes i jest waznym aspektem tworzenia elastycznych komponentow.
Atrybuty przekazane do komponentu, ktore NIE sa zadeklarowane jako props ani emity, automatycznie trafiaja do root elementu szablonu:
1<!-- Rodzic -->
2<CustomInput class="special" data-testid="email" />
3
4<!-- CustomInput.vue (root = input) -->
5<template>
6 <input type="text" />
7</template>
8
9<!-- Wynik w DOM -->
10<input type="text" class="special" data-testid="email" />Dotyczy to:
class, style, id, data-*, event listenerow i innych atrybutow HTML. To zachowanie jest bardzo przydatne - pozwala uzytkownikowi komponentu dodawac klasy CSS, style inline czy atrybuty dostepnosci bez koniecznosci definiowania ich jako props.Jesli root element komponentu ma juz wlasne
class lub style, Vue automatycznie je laczy (merge) z atrybutami z rodzica:1<!-- Rodzic -->
2<StatusPanel class="highlighted" style="margin-top: 10px" />
3
4<!-- StatusPanel.vue -->
5<template>
6 <div class="panel" style="padding: 1rem">...</div>
7</template>
8
9<!-- Wynik w DOM - klasy i style polaczone -->
10<div class="panel highlighted" style="padding: 1rem; margin-top: 10px">...</div>Listenery zdarzen rowniez przechodza. Jesli rodzic przekaze
@click, zostanie on dodany do root elementu:1<!-- Rodzic -->
2<MarsButton @click="handleClick" />
3
4<!-- MarsButton.vue -->
5<template>
6 <button @click="logInternal">Kliknij</button>
7</template>W tym przypadku klikniecie przycisku wywoła oba handlery - najpierw
logInternal z komponentu, potem handleClick z rodzica.Gdy chcesz kontrolowac dokladnie, gdzie trafiaja atrybuty (np. chcesz je przypisac do wewnetrznego elementu zamiast root), uzyj
inheritAttrs: false:1<script setup>
2defineOptions({ inheritAttrs: false })
3</script>
4
5<template>
6 <div class="wrapper">
7 <!-- Reczne przypisanie do konkretnego elementu -->
8 <input v-bind="$attrs" type="text" />
9 </div>
10</template>To jest czesty wzorzec w komponentach formularzy - wrapper
<div> sluzy do stylowania, a $attrs trafiaja bezposrednio na <input>, gdzie sa najbardziej uzyteczne (np. placeholder, disabled, aria-label).Dostep do atrybutow w
<script setup> uzyskujesz przez composable useAttrs():1<script setup>
2import { useAttrs } from 'vue'
3
4const attrs = useAttrs()
5
6console.log(attrs.class) // "special"
7console.log(attrs['data-testid']) // "email"
8console.log(attrs.style) // obiekt stylow
9</script>Uwaga:
useAttrs() NIE jest reaktywne - jesli potrzebujesz reagowac na zmiany atrybutow, uzyj $attrs bezposrednio w szablonie lub onUpdated hook.Gdy komponent ma wiele root elementow (fragments), Vue nie wie, do ktorego przypisac fallthrough attributes. Musisz jawnie przypisac
$attrs do wybranego elementu:1<script setup>
2defineOptions({ inheritAttrs: false })
3</script>
4
5<template>
6 <header>Header</header>
7 <main v-bind="$attrs">Content</main>
8 <footer>Footer</footer>
9</template>Bez
inheritAttrs: false i jawnego v-bind="$attrs", Vue wyswietli ostrzezenie w konsoli. To wymaga swiadomej decyzji - ktory element ma "dziedziczyc" atrybuty z rodzica.Polaczmy wszystkie koncepcje w jednym przykladzie:
1<script setup>
2import { useAttrs } from 'vue'
3
4defineOptions({ inheritAttrs: false })
5
6defineProps({ title: String })
7
8const attrs = useAttrs()
9const hasCustomClass = !!attrs.class
10</script>
11
12<template>
13 <section class="nova-panel">
14 <h3>{{ title }}</h3>
15 <div v-bind="$attrs" class="panel-content">
16 <slot />
17 </div>
18 <small v-if="hasCustomClass">Custom styling applied</small>
19 </section>
20</template>Dzieki temu wzorcowi rodzic moze przekazac
class, style, id i inne atrybuty, ktore trafia dokladnie na panel-content, a nie na zewnetrzna <section>.