Attribute Inheritance — Vue

Di Vue, atribut yang diberikan ke komponen tapi tidak dideklarasikan sebagai props akan otomatis diteruskan ke elemen root komponen. Ini disebut fallthrough…

Atribut yang kamu tulis di tag sebuah komponen tapi bukan prop, misalnya class, id, style, type, atau @click, otomatis diteruskan ke root element komponen itu, yaitu elemen paling luar di template-nya. Vue menyebutnya fallthrough attributes. Bayangin kasir Warung Bu Sri menempel stiker "PEDAS" di pesanan bungkus. Stikernya nempel di kertas pembungkus paling luar, bukan di nasinya. Kalau pesanannya dua bungkus yang terpisah, kasir bingung harus menempel di yang mana, jadi stikernya gak ditempel sama sekali.

🧪 Tebakan cepat: Template komponen Tombol cuma berisi <button class="tombol">Pesan</button>. Parent menulis <Tombol class="utama" id="pesan" />. HTML apa yang keluar? Tebak dulu, jawabannya ada di bagian "Atribut jatuh ke root element".

Atribut jatuh ke root element

<!-- Tombol.vue -->
<template>
  <button class="tombol" type="button">Pesan</button>
</template>
<!-- App.vue -->
<Tombol class="utama" id="pesan" type="submit" style="color: white" @click="pesan" />

HTML yang keluar:

<button class="tombol utama" type="submit" id="pesan" style="color: white;">Pesan</button>

$attrs: semua yang diteruskan

Semua atribut yang diteruskan dikumpulkan di objek $attrs. Di template child, {{ $attrs }} untuk tag tadi berisi class, id, type, style, dan onClick. Di <script setup>, objek yang sama didapat dengan useAttrs() yang di-import dari "vue".

Mematikan pewarisan: inheritAttrs: false

Kadang root element bukan tempat yang tepat. Misalnya Tombol membungkus tombolnya dengan <div>, dan class atau id dari parent seharusnya jatuh ke <button>, bukan ke <div>:

<!-- Tombol.vue -->
<script setup>
defineOptions({ inheritAttrs: false })
</script>

<template>
  <div class="bungkus">
    <button v-bind="$attrs">Pesan</button>
  </div>
</template>

Hasilnya untuk <Tombol class="utama" id="pesan" @click="pesan" />:

<div class="bungkus"><button class="utama" id="pesan">Pesan</button></div>

Banyak root: gak ada yang dapat

<!-- Tombol.vue, dua root -->
<template>
  <button>Pesan</button>
  <small>Habis jam 9</small>
</template>

<Tombol class="utama" id="pesan" /> menghasilkan <button>Pesan</button><small>Habis jam 9</small> tanpa class dan id. Vue gak tahu harus menempel di root yang mana, jadi gak menempel sama sekali. Build development menulis [Vue warn]: Extraneous non-props attributes (class, id) were passed to component but could not be automatically inherited because component renders fragment or text or teleport root nodes. Obatnya: tulis v-bind="$attrs" di root yang harus menerimanya, dan warning-nya hilang.

File .vue dan editor latihan

Di editor, child ditulis sebagai objek dan dipasang dengan <component :is>. Atribut yang kamu tulis di <component> diteruskan dengan cara yang sama:

const jumlah = ref(0)

const Tombol = {
  template: `<button class="tombol">Pesan</button>`,
}

---template---
<div>
  <component :is="Tombol" class="utama" id="pesan" @click="jumlah++" />
  <p>Dipesan: {{ jumlah }}</p>
</div>
Di file .vue Di editor latihan
defineOptions({ inheritAttrs: false }) opsi inheritAttrs: false di objek Tombol
useAttrs() di script $attrs di template child
<Tombol class="utama" /> <component :is="Tombol" class="utama" />

Latihannya memakai bentuk ini. Starter-nya punya Tombol dengan template <button class="btn">Klik Saya</button>, tanpa id, dan kamu memasangnya dengan <component :is="Tombol" id="btn" class="primary" />. id itu jatuh ke <button>, dan primary digabung dengan btn. Pemeriksanya membaca button#btn.btn.primary, dan menolak id yang ditulis langsung di <button>.

⚠️ Jebakan yang sering ketemu

TL;DR: Atribut di tag komponen yang bukan prop atau emits, seperti class, id, style, dan @click, diteruskan ke root element komponen itu. class dan style digabung, atribut lain dari parent menimpa milik child, dan listener ikut dipasang. Semuanya terkumpul di $attrs (useAttrs() di script). inheritAttrs: false mematikan penerusan otomatis, lalu v-bind="$attrs" menempelkannya ke elemen pilihanmu. Komponen dengan banyak root gak meneruskan apa pun sampai kamu menulis v-bind="$attrs" sendiri.

Yang akan kamu pelajari