Vue Dasar

Target: pemula yang sudah memahami JavaScript dasar, lalu ingin Versi: Vue 3.x (Composition API) Prasyarat: JavaScript Dasar · JavaScript DOM Fokus modul pembelajaran ini: **template → state → reactive → computed → Semua contoh utama menggunakan <script setup> agar cocok dengan gaya Vue

Cara Belajar

🟢 Fundamental
→ wajib untuk mulai membuat UI dengan Vue
 
🟡 Lanjutan
→ pelajari setelah fundamental nyaman
 
🔴 Advanced / Reference
→ penting ketika kebutuhan aplikasi meningkat

Mental model:

STATE / DATA
     |
     v
  TEMPLATE
     |
     v
     DOM

Daftar Isi

🟢 Fundamental

  1. Pengenalan
  2. Membuat Project
  3. Hello Vue
  4. API Style
  5. Template
  6. Template Attributes
  7. JS Expression di Template
  8. Directive
  9. State
  10. DOM Update
  11. Reactive
  12. Computed Properties
  13. Style
  14. Conditional Rendering
  15. List Rendering
  16. Event Handling
  17. Input Binding

🟡 Lanjutan

  1. Watchers
  2. Template Refs
  3. Lifecycle Hooks
  4. Component
  5. Component Props
  6. Component Event
  7. Component Model
  8. Fallthrough Attributes
  9. Component Slot
  10. Dynamic Component
  11. Provide dan Inject

🔴 Advanced / Reference

  1. Component Instance
  2. Peta Ingatan Cepat
  3. Tabel Ringkasan
  4. Cheat Code Vue 10 Detik
  5. Urutan Belajar yang Disarankan
  6. Mini Project
  7. Referensi Resmi

1. 🟢 Pengenalan

Vue.js adalah framework JavaScript untuk membuat UI.

Cara berpikir sederhananya:

STATE / DATA


  TEMPLATE


     DOM

Ketika state berubah:

state berubah

Vue mendeteksi perubahan

DOM diperbarui

Contoh:

<script setup>
import { ref } from 'vue'
 
const count = ref(0)
</script>
 
<template>
  <button @click="count++">
    Count: {{ count }}
  </button>
</template>

Output awal:

Count: 0

Klik tombol:

Count: 1
Count: 2
Count: 3
...

Kunci: Vue membuat UI mengikuti state.


2. 🟢 Membuat Project

Cara umum membuat project Vue modern menggunakan Vite:

npm create vue@latest

Ikuti pertanyaan setup, kemudian:

cd nama-project
npm install
npm run dev

Struktur sederhananya:

project/
├── src/
│   ├── components/
│   ├── App.vue
│   └── main.js
├── public/
├── index.html
├── package.json
└── vite.config.js

File penting:

main.js


App.vue

   ├── Component A
   ├── Component B
   └── Component C

Kunci:

main.js → titik masuk
App.vue  → root component
*.vue    → component

3. 🟢 Hello Vue

Component Vue biasanya memiliki:

<script setup>
const message = 'Hello Vue!'
</script>
 
<template>
  <h1>{{ message }}</h1>
</template>

Output:

Hello Vue!

Vue Single File Component biasanya terdiri dari:

┌─────────────────────────┐
│ <script setup>          │
│   JavaScript            │
├─────────────────────────┤
│ <template>              │
│   HTML-like template    │
├─────────────────────────┤
│ <style scoped>          │
│   CSS                   │
└─────────────────────────┘

4. 🟢 API Style

Vue menyediakan dua gaya utama:

Options API

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
 
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

Composition API

Gaya modern yang sering digunakan bersama <script setup>:

<script setup>
import { ref } from 'vue'
 
const count = ref(0)
 
function increment() {
  count.value++
}
</script>
Perbandingan
Options API
data
methods
computed
watch

dikelompokkan berdasarkan jenis fitur
 
Composition API
ref
computed
watch
function

dikelompokkan berdasarkan logika/fitur

Kunci: untuk belajar Vue modern, prioritaskan Composition API + <script setup>.


5. 🟢 Template

Template adalah bagian HTML-like tempat kita mendeskripsikan struktur UI yang akan ditampilkan ke layar.

<script setup>
const name = 'Budi'
</script>
 
<template>
  <h1>Halo {{ name }}</h1>
  <p>Selamat belajar Vue.</p>
</template>

Output:

Halo Budi
Selamat belajar Vue.
Text Interpolation

Syntax {{ }} (Mustache syntax) disebut text interpolation. Ini adalah cara paling dasar untuk menyisipkan data atau variabel JavaScript ke dalam teks HTML secara dinamis.

<p>Halo, {{ name }}!</p>

Vue akan merender nilai variabel name sebagai teks polos (plain text). Jika nilai name berubah, tampilan teks di browser otomatis diperbarui.

Kunci: {{ }} digunakan untuk menyisipkan teks dinamis ke dalam elemen HTML.


6. 🟢 Template Attributes

Attribute binding adalah mekanisme untuk menghubungkan atribut elemen HTML (seperti src, href, title, disabled, class, dan style) dengan nilai data atau ekspresi JavaScript secara dinamis menggunakan directive v-bind atau shorthand titik dua (:).

<script setup>
import { ref } from 'vue'
 
const imageUrl = '/logo.png'
const title = 'Logo Vue'
const isDisabled = ref(true)
</script>
 
<template>
  <!-- Shorthand :src sama artinya dengan v-bind:src -->
  <img :src="imageUrl" :alt="title">
 
  <!-- Binding boolean attribute -->
  <button :disabled="isDisabled">
    Tombol Nonaktif
  </button>
</template>

Output di DOM:

<img src="/logo.png" alt="Logo Vue">
<button disabled>Tombol Nonaktif</button>
Atribut Boolean

Untuk atribut boolean seperti disabled, checked, atau readonly:

  • Jika nilainya truthy (true, 'yes'), atribut akan disertakan pada elemen HTML.
  • Jika nilainya falsy (false, null, undefined), atribut otomatis dihilangkan oleh Vue.

Kunci:

:src="..."      → binding atribut src
:disabled="..." → binding atribut boolean
:class="..."    → dynamic class
:style="..."    → dynamic style

: adalah shorthand resmi untuk v-bind:.


7. 🟢 JS Expression di Template

Template dapat menjalankan expression JavaScript sederhana.

<script setup>
const name = 'Budi'
const age = 20
</script>
 
<template>
  <p>{{ name.toUpperCase() }}</p>
  <p>{{ age + 1 }}</p>
  <p>{{ age >= 18 ? 'Dewasa' : 'Anak' }}</p>
</template>

Output:

BUDI
21
Dewasa

Gunakan expression sederhana.

Untuk logika kompleks, lebih baik gunakan:

computed
method/function

daripada membuat template terlalu rumit.


8. 🟢 Directive

Directive adalah atribut HTML khusus bawaan Vue yang selalu diawali dengan awalan v-. Directive memberikan instruksi khusus kepada Vue untuk memanipulasi elemen DOM ketika nilai ekspresinya berubah.

Anatomi Directive
v-on:click.prevent="submit"
──┬─ ──┬── ───┬───  ───┬──
  │    │      │        └─ Value: ekspresi / handler JavaScript yang dijalankan
  │    │      └────────── Modifier: postfix khusus untuk mengubah perilaku event/binding
  │    └───────────────── Argument: target nama event atau nama attribute HTML
  └────────────────────── Directive Name: nama directive bawaan Vue (v-on, v-bind, dll.)
Daftar Directive Umum
v-bind  (:)       → menghubungkan attribute HTML ke JavaScript
v-on    (@)       → mendengarkan event DOM (klik, submit, keyup)
v-model           → two-way data binding pada form input
v-if / v-else     → conditional rendering (tambah/hapus elemen di DOM)
v-show            → conditional display (toggle display: none)
v-for             → list rendering (perulangan data array)
v-html            → merender raw HTML (hati-hati risiko XSS)
v-text            → mengupdate textContent elemen
Shorthand Paling Sering Digunakan
v-bind:src="url"     → :src="url"
v-on:click="handler" → @click="handler"

Contoh:

<!-- Menggunakan shorthand resmi -->
<button @click="count++">
  Tambah
</button>

Kunci:

: → shorthand v-bind (attribute)
@ → shorthand v-on (event)
# → shorthand v-slot (slot)

9. 🟢 State

State adalah data yang menentukan kondisi dan tampilan UI. Ketika data state berubah, Vue akan otomatis memperbarui tampilan UI yang menggunakan data tersebut.

Gunakan ref() untuk membuat reactive state (baik tipe primitif seperti string/number/boolean maupun object).

<script setup>
import { ref } from 'vue'
 
const count = ref(0)
 
function increment() {
  count.value++
}
</script>
 
<template>
  <p>Total: {{ count }}</p>
  <button @click="increment">Tambah</button>
</template>

Output awal:

Total: 0

Setelah klik tombol:

Total: 1
Mengapa .value?

ref() membungkus nilai data ke dalam sebuah objek reaktif dengan properti .value.

Aturan akses ref():

  1. Di JavaScript (<script>): wajib menggunakan .value untuk membaca atau mengubah data:
    console.log(count.value) // membaca nilai
    count.value++            // mengubah nilai
  2. Di template (<template>): tidak perlu .value karena Vue otomatis melakukan unwrapping (auto-unwrap):
    <p>{{ count }}</p>

Kunci:

ref()

.value saat di <script> (JavaScript)
tanpa .value saat di <template> (HTML)

10. 🟢 DOM Update

Vue memperbarui DOM secara otomatis ketika reactive state berubah.

<script setup>
import { ref } from 'vue'
 
const message = ref('Halo')
 
function changeMessage() {
  message.value = 'Halo Vue!'
}
</script>
 
<template>
  <p>{{ message }}</p>
  <button @click="changeMessage">
    Ubah
  </button>
</template>

Sebelum klik:

Halo

Setelah klik:

Halo Vue!

Diagram:

message.value berubah


Vue Reactive System


DOM Update (Asynchronous Batch)
Update Asynchronous & nextTick()

Ketika data state berubah, Vue tidak langsung mengupdate DOM saat itu juga secara sinkron, melainkan mengumpulkannya dalam antrean (buffer) dan melakukan batch update secara asynchronous agar performa rendering tetap efisien.

Jika Anda perlu mengakses elemen DOM tepat setelah DOM selesai diperbarui (misalnya mengambil ukuran tinggi elemen baru atau memfokuskan input), gunakan fungsi nextTick():

<script setup>
import { ref, nextTick } from 'vue'
 
const message = ref('Halo')
 
async function changeMessage() {
  message.value = 'Halo Vue!'
  
  // Tunggu hingga Vue selesai mengupdate DOM
  await nextTick()
  console.log('DOM sudah selesai diperbarui!')
}
</script>

Kunci: Vue melakukan DOM update secara asynchronous; gunakan await nextTick() jika butuh menunggu DOM selesai diperbarui.


11. 🟢 Reactive

reactive() adalah fungsi alternatif untuk membuat objek atau array menjadi reactive secara mendalam (deep reactive).

Berbeda dengan ref(), reactive() tidak membungkus data dalam .value, melainkan langsung mengembalikan JavaScript Proxy dari objek tersebut.

<script setup>
import { reactive } from 'vue'
 
const user = reactive({
  name: 'Budi',
  age: 20
})
 
function birthday() {
  user.age++
}
</script>
 
<template>
  <p>{{ user.name }}</p>
  <p>{{ user.age }}</p>
 
  <button @click="birthday">
    Ulang Tahun
  </button>
</template>

Output awal:

Budi
20

Klik:

Budi
21
Keterbatasan reactive()

Meskipun terlihat lebih praktis tanpa .value, reactive() memiliki beberapa keterbatasan penting karena menggunakan JavaScript Proxy:

  1. Hanya untuk tipe objek: Tidak bisa digunakan untuk data primitif (string, number, boolean).
    // ❌ Error / tidak bekerja
    const count = reactive(0)
  2. Tidak boleh di-reassign: Mengganti seluruh objek akan memutus koneksi reaktivitas Proxy aslinya.
    let user = reactive({ name: 'Budi' })
    // ❌ Reaktivitas putus!
    user = reactive({ name: 'Andi' })
  3. Kehilangan reaktivitas saat destructuring:
    const user = reactive({ name: 'Budi', age: 20 })
    // ❌ `name` menjadi string biasa, bukan reactive lagi
    const { name } = user
ref vs reactive
ref(value)

Bisa untuk tipe data apa saja (primitif & objek)
Akses lewat .value di script
Best practice standar Vue 3
 
reactive(object)

Hanya untuk objek/array
Akses langsung tanpa .value
Bekerja dengan JavaScript Proxy

Kunci: Gunakan ref() sebagai pilihan utama (default) untuk semua kebutuhan state di Vue 3 modern.


12. 🟢 Computed Properties

computed() digunakan untuk membuat nilai turunan dari reactive state.

<script setup>
import { ref, computed } from 'vue'
 
const price = ref(10000)
const quantity = ref(3)
 
const total = computed(() => {
  return price.value * quantity.value
})
</script>
 
<template>
  <p>Total: {{ total }}</p>
</template>

Output:

Total: 30000

Diagram:

price (10000) ────┐
                  ├──> computed(() => ...) ──> total (30000)
quantity (3)  ────┘
Keunggulan computed: Caching Otomatis

Mengapa menggunakan computed() daripada fungsi/method biasa di template?

  1. Caching Berdasarkan Dependensi: computed() mengingat (cache) hasil perhitungannya. Selama nilai price dan quantity tidak berubah, pemanggilan total berkali-kali akan langsung mengambil hasil cache tanpa menghitung ulang.
  2. Method Tanpa Cache: Jika kita memanggil fungsi biasa getTotal() di template, fungsi tersebut akan dieksekusi ulang setiap kali terjadi re-render pada komponen, meskipun datanya tidak berubah.
Sifat Default: Read-Only

Secara default, nilai computed bersifat read-only (hanya bisa dibaca, tidak boleh di-assign langsung):

// ❌ Peringatan / Error di console
total.value = 50000

Kunci:

state → computed → nilai turunan (dengan caching)

Gunakan computed untuk menghitung nilai/tampilan, bukan untuk menjalankan side effect (untuk side effect gunakan watch).


13. 🟢 Style

Vue menyediakan integrasi khusus untuk manipulasi atribut class dan style menggunakan data JavaScript dinamis (:class dan :style).

1. Class Binding: Object Syntax

Class akan diterapkan jika nilai propertinya bernilai truthy (true):

<script setup>
import { ref } from 'vue'
 
const isActive = ref(true)
const hasError = ref(false)
</script>
 
<template>
  <div :class="{ active: isActive, 'text-danger': hasError }">
    Menu Item
  </div>
</template>
 
<style>
.active {
  color: green;
  font-weight: bold;
}
.text-danger {
  color: red;
}
</style>

Output di DOM:

<div class="active">Menu Item</div>
2. Class Binding: Array Syntax

Gunakan array jika ingin menerapkan beberapa class CSS secara bersamaan:

<script setup>
import { ref } from 'vue'
 
const activeClass = ref('active')
const sizeClass = ref('text-large')
</script>
 
<template>
  <div :class="[activeClass, sizeClass]">
    Konten Utama
  </div>
</template>

Output di DOM:

<div class="active text-large">Konten Utama</div>
3. Inline Style Binding

Gunakan objek JavaScript untuk mengatur properti CSS inline:

<script setup>
import { ref } from 'vue'
 
const textColor = ref('blue')
const fontSize = ref(18)
</script>
 
<template>
  <div :style="{ color: textColor, fontSize: fontSize + 'px' }">
    Teks Berwarna
  </div>
</template>

Output di DOM:

<div style="color: blue; font-size: 18px;">Teks Berwarna</div>

Kunci:

:class="{ active: isActive }"     → toggle class berdasarkan kondisi boolean
:class="[classA, classB]"         → gabungkan beberapa class
:style="{ color: textColor }"     → styling CSS inline dinamis

14. 🟢 Conditional Rendering

Conditional rendering adalah teknik untuk menampilkan, menyembunyikan, atau mengganti elemen UI di layar berdasarkan kondisi logika (truthy/falsy) dari state.

Vue menyediakan beberapa directive untuk conditional rendering:

v-if      → render elemen jika kondisi true
v-else-if → kondisi alternatif
v-else    → fallback jika semua kondisi di atas false
v-show    → toggle tampilan elemen (CSS display)

Contoh:

<script setup>
import { ref } from 'vue'
 
const isLoggedIn = ref(false)
</script>
 
<template>
  <p v-if="isLoggedIn">Dashboard</p>
  <p v-else>Silakan login</p>
</template>

Output:

Silakan login
v-if vs v-show
v-if

Benar-benar menambah atau menghapus elemen dari DOM tree.
Lebih hemat biaya render awal jika kondisi awal false.
 
v-show

Elemen selalu ada di DOM, hanya mengubah CSS display: none.
Lebih hemat biaya jika elemen sangat sering di-toggle (tampil/sembunyi).

Kunci: Gunakan v-if untuk percabangan kondisional umum, gunakan v-show jika elemen sangat sering di-toggle.


15. 🟢 List Rendering

List rendering adalah teknik untuk merender daftar elemen berulang (seperti daftar produk, daftar pengguna, atau baris tabel) dari data Array atau Objek ke template HTML menggunakan directive v-for.

<script setup>
const users = [
  { id: 1, name: 'Budi' },
  { id: 2, name: 'Andi' },
  { id: 3, name: 'Siti' }
]
</script>
 
<template>
  <ul>
    <li v-for="user in users" :key="user.id">
      {{ user.name }}
    </li>
  </ul>
</template>

Output:

Budi
Andi
Siti

Diagram:

users

  ├── Budi
  ├── Andi
  └── Siti


    v-for


  <li>...</li>
Sangat penting: key
:key="user.id"

Selalu sertakan atribut :key yang unik dan stabil (seperti ID database) pada setiap item v-for. Atribut key membantu algoritma Virtual DOM Vue melacak identitas setiap elemen secara efisien saat urutan data berubah, ditambah, atau dihapus.

Kunci: Gunakan v-for="item in items" :key="item.id" untuk merender data list berulang.


16. 🟢 Event Handling

Event handling adalah mekanisme untuk mendengarkan (listen) dan merespons aksi atau interaksi dari pengguna (seperti klik tombol, input keyboard, hover mouse, atau submit form) menggunakan directive v-on atau shorthand @.

<script setup>
function sayHello() {
  alert('Halo!')
}
</script>
 
<template>
  <button @click="sayHello">
    Klik
  </button>
</template>
Event Object

Handler fungsi dapat menerima parameter objek event bawaan browser secara otomatis:

<button @click="handleClick">
  Lihat Target
</button>
function handleClick(event) {
  console.log(event.target)
}
Event Modifier

Vue menyediakan modifier event untuk mempermudah penanganan perilaku default event tanpa perlu memanggil event.preventDefault() atau event.stopPropagation() secara manual:

<!-- Mencegah reload halaman saat submit form -->
<form @submit.prevent="submit">
<!-- Menghentikan event bubbling -->
<button @click.stop="click">

Modifier umum:

.prevent → memanggil event.preventDefault()
.stop    → memanggil event.stopPropagation()
.self    → hanya trigger jika target event adalah elemen itu sendiri
.once    → hanya trigger event 1 kali saja
.capture → menggunakan event capture mode

Key modifier untuk mendengarkan tombol keyboard tertentu:

<input @keyup.enter="submit">

Kunci: Gunakan @event="handler" untuk menangani event interaksi pengguna.


17. 🟢 Input Binding

Input binding adalah mekanisme untuk menghubungkan nilai elemen form input (seperti input teks, textarea, checkbox, radio, dan select dropdown) dengan reactive state secara dua arah (two-way data binding) menggunakan directive v-model.

Ketika pengguna mengetik di form UI, data state otomatis berubah. Sebaliknya, ketika data state diubah lewat kode JavaScript, tampilan form di UI otomatis diperbarui.

<script setup>
import { ref } from 'vue'
 
const name = ref('')
</script>
 
<template>
  <input v-model="name">
  <p>Halo {{ name }}</p>
</template>

Jika user mengetik:

Budi

Output:

Halo Budi

Diagram:

User mengetik di UI


  v-model (Two-way Binding)


  name.value (State)


  Template / DOM terupdate
Penggunaan pada Berbagai Tipe Input

v-model secara cerdas menyesuaikan properti DOM yang di-bind sesuai tipe inputnya:

<!-- Input Text -->
<input v-model="text">
 
<!-- Checkbox (boolean) -->
<input type="checkbox" v-model="checked">
 
<!-- Select Dropdown -->
<select v-model="selected">
  <option value="A">Opsi A</option>
  <option value="B">Opsi B</option>
</select>

Kunci: v-model menyederhanakan two-way data binding antara form input dan reactive state.


18. 🟡 Watchers

watch() digunakan untuk menjalankan side effect ketika state berubah.

<script setup>
import { ref, watch } from 'vue'
 
const count = ref(0)
 
watch(count, (newValue, oldValue) => {
  console.log(oldValue, '→', newValue)
})
 
count.value = 1

Konsep:

count berubah

watch mendeteksi

callback dijalankan

Gunakan watcher untuk hal seperti:

API request
localStorage
logging
side effect

Jangan gunakan watch hanya untuk menghitung nilai turunan. Untuk itu gunakan computed.

Kunci:

 computed = menghasilkan nilai baru berdasarkan data lain.
 watch = melakukan sesuatu ketika data berubah.

19. 🟡 Template Refs

Template ref digunakan untuk mendapatkan referensi langsung ke elemen HTML (DOM) atau instance komponen anak tanpa harus menggunakan document.querySelector().

<script setup>
import { ref, onMounted } from 'vue'
 
// Inisialisasi ref dengan nilai awal null
const input = ref(null)
 
onMounted(() => {
  // DOM element sudah siap dan dapat diakses
  input.value.focus()
})
</script>
 
<template>
  <!-- Menghubungkan variabel ref dengan elemen HTML -->
  <input ref="input" placeholder="Otomatis fokus saat halaman dimuat">
</template>

Diagram:

Deklarasi di <script>
  const input = ref(null) ── (awal: null)

          │ template rendering

<input ref="input"> ─────── (elemen DOM terpasang)

          │ onMounted()

input.value ───────────────> HTMLInputElement (bisa di-focus(), dll.)
Mengapa Nilai Awalnya null?

Saat baris <script setup> pertama kali dieksekusi, komponen baru saja diinisialisasi dan elemen HTML belum dibuat di DOM fisik. Oleh karena itu, variabel ref harus diinisialisasi dengan null.

Referensi DOM baru akan terisi oleh Vue setelah komponen selesai dirender dan dipasang ke DOM (onMounted()).

Kunci: Template ref hanya dapat diakses setelah komponen masuk ke tahap onMounted().


20. 🟡 Lifecycle Hooks

Lifecycle adalah tahapan hidup sebuah component Vue, mulai dari component dibuat, ditampilkan, diperbarui, sampai dihapus.

Yang sering digunakan:

onMounted
onUpdated
onUnmounted

Contoh:

<script setup>
import {
  onMounted,
  onUpdated,
  onUnmounted
} from 'vue'
 
onMounted(() => {
  console.log('Mounted')
})
 
onUpdated(() => {
  console.log('Updated')
})
 
onUnmounted(() => {
  console.log('Unmounted')
})
</script>

Diagram:

        ┌───────────────────┐
        │ Component dibuat  │
        └─────────┬─────────┘


            ┌──────────────┐
            │ onMounted()  │
            │              │
            │ masuk DOM    │
            └─────┬────────┘

                  │ Component Hidup

        ┌─────────────────────────┐
        │    State berubah        │
        │         │               │
        │         ▼               │
        │    onUpdated()          │
        │         │               │
        │         │               │
        │    State berubah lagi   │
        │         │               │
        │         ▼               │
        │    onUpdated()          │
        │         │               │
        │        ...              │
        └─────────┬───────────────┘

                  │ Component dihapus

          ┌──────────────────┐
          │ onUnmounted()    │
          │                  │
          │ setelah dilepas  │
          └──────────────────┘

Kunci:

onMounted   → setelah component masuk DOM
onUpdated   → setelah component/DOM diperbarui
              dan bisa terjadi berkali-kali
onUnmounted → setelah component dilepas

Urutan sederhananya:

Component dibuat


 onMounted()


Component hidup

      ├──── state berubah ────→ onUpdated()
      │                              │
      │                              └── bisa berulang


Component dihapus


onUnmounted()

Referensi: Lifecycle Hooks & Lifecycle Diagram


21. 🟡 Component

Component adalah bagian UI yang dapat digunakan kembali.

UserCard.vue:

<script setup>
defineProps({
  name: String
})
</script>
 
<template>
  <div>
    User: {{ name }}
  </div>
</template>

Parent:

<script setup>
import UserCard from './components/UserCard.vue'
</script>
 
<template>
  <UserCard name="Budi" />
  <UserCard name="Andi" />
</template>

Output:

User: Budi
User: Andi

Diagram:

App

 ├── UserCard("Budi")

 └── UserCard("Andi")

22. 🟡 Component Props

Props adalah data yang dikirim parent → child.

Child:

<script setup>
defineProps({
  name: String,
  age: Number
})
</script>
 
<template>
  <p>{{ name }} - {{ age }}</p>
</template>

Parent:

<UserCard
  name="Budi"
  :age="20"
/>

Output:

Budi - 20
Dengan TypeScript
<script setup lang="ts">
interface Props {
  name: string
  age: number
}
 
defineProps<Props>()
</script>

Diagram:

Parent

  │ props

Child

Kunci: props = input component.


23. 🟡 Component Event

Component Event digunakan untuk komunikasi dari child component ke parent component melalui custom event (emit).

Child:

<script setup>
const emit = defineEmits(['save'])
 
function save() {
  emit('save', 'Data dari child')
}
</script>
 
<template>
  <button @click="save">
    Save
  </button>
</template>

Parent:

<script setup>
function handleSave(data) {
  console.log(data)
}
</script>
 
<template>
  <Child @save="handleSave" />
</template>

Ketika button diklik:

              Child


                │ emit('save', data)

        ┌───────────────────────┐
        │  Event "save" + data  │
        └────────┬──────────────┘


              Parent

                 │ @save

         handleSave(data)

Hubungan dengan props:

       ┌──────────────┐
       │    Parent    │
       └──────┬───────┘
              │       ▲
        props │       │ emit event
              ▼       │
       ┌──────────────┐
       │    Child     │
       └──────────────┘

Lebih Sederhananya:

props

  │ data

Parent ─────────→ Child
 
 
emit

  │ event + data

Child ──────────→ Parent

Kunci:

props → parent ke child
emit  → child ke parent

24. 🟡 Component Model

v-model pada komponen kustom digunakan untuk membuat komunikasi dua arah (two-way data binding) antara parent dan child component.

1. Cara Modern: defineModel() (Vue 3.4+)

defineModel() adalah makro resmi bawaan Vue yang menyederhanakan two-way binding menjadi sangat ringkas:

Child Component (CustomInput.vue):

<script setup>
// defineModel() otomatis mengelola prop dan emit internal
const model = defineModel()
</script>
 
<template>
  <input v-model="model" placeholder="Ketik sesuatu...">
</template>

Parent Component:

<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
 
const username = ref('Budi')
</script>
 
<template>
  <!-- v-model sinkron otomatis 2 arah dengan CustomInput -->
  <CustomInput v-model="username" />
  <p>Nama: {{ username }}</p>
</template>

Output:

Nama: Budi

2. Cara Konvensional (Vue 3.3 ke Bawah)

Di balik layar, v-model pada komponen bekerja menggunakan pasangan prop modelValue dan event update:modelValue:

Child Component (UserInput.vue):

<script setup>
defineProps({
  modelValue: String
})
 
const emit = defineEmits(['update:modelValue'])
</script>
 
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  >
</template>

Diagram Alur Kerja:

Parent (username)

       │ :modelValue (Prop ke bawah)

Child (<input>)

       │ @update:modelValue (Emit ke atas)

Parent (username terupdate)

Kunci:

Vue 3.4+         → gunakan defineModel() (paling bersih dan direkomendasikan)
Di balik layar   → prop modelValue + emit update:modelValue

25. 🟡 Fallthrough Attributes

Fallthrough attribute adalah atribut atau event listener yang dikirimkan ke komponen dari parent, tetapi tidak dideklarasikan secara eksplisit di defineProps() atau defineEmits().

Secara default, atribut-atribut ini akan otomatis diteruskan (fallthrough) ke elemen root dari komponen anak.

Child Component (MyButton.vue):

<template>
  <!-- class, id, style, dan event listener akan otomatis jatuh ke root <button> -->
  <button class="btn">
    Simpan
  </button>
</template>

Parent Component:

<template>
  <MyButton
    id="save-btn"
    class="btn-primary"
    disabled
    @click="handleClick"
  />
</template>

Output di DOM:

<button id="save-btn" class="btn btn-primary" disabled>Simpan</button>

Menonaktifkan Fallthrough: inheritAttrs: false

Jika Anda tidak ingin atribut otomatis jatuh ke root element, matikan fitur ini menggunakan defineOptions:

<script setup>
defineOptions({
  inheritAttrs: false
})
</script>
 
<template>
  <div class="wrapper">
    <!-- Tempelkan atribut secara eksplisit ke elemen yang diinginkan menggunakan $attrs -->
    <button v-bind="$attrs">Tombol Kustom</button>
  </div>
</template>
Multi-Root Components (Fragments)

Jika komponen anak memiliki lebih dari satu elemen root di <template>, Vue tidak tahu elemen mana yang harus menerima atribut fallthrough dan akan menampilkan peringatan di konsol.

Solusinya: gunakan v-bind="$attrs" pada elemen yang dituju secara eksplisit:

<!-- Multi-root: wajib tentukan elemen target dengan v-bind="$attrs" -->
<template>
  <header>Header</header>
  <main v-bind="$attrs">Konten Utama</main>
  <footer>Footer</footer>
</template>

Kunci: Fallthrough attribute otomatis jatuh ke single-root element; gunakan v-bind="$attrs" untuk kontrol manual atau pada multi-root components.


26. 🟡 Component Slot

Slot digunakan untuk mengirim isi/template dari parent ke child.

Child:

<template>
  <div class="card">
    <slot />
  </div>
</template>

Parent:

<Card>
  <h2>Hello</h2>
  <p>Isi card</p>
</Card>

Hasil konsep:

Card
┌──────────────────┐
│ Hello            │
│ Isi card         │
└──────────────────┘
Named slot

Child:

<template>
  <header>
    <slot name="header" />
  </header>
 
  <main>
    <slot />
  </main>
</template>

Parent:

<Card>
  <template #header>
    <h1>Judul</h1>
  </template>
 
  <p>Isi</p>
</Card>

Kunci:

props → kirim data
slot  → kirim template/isi UI

27. 🟡 Dynamic Component

Dynamic component digunakan untuk mengganti component secara dinamis.

<script setup>
import { ref } from 'vue'
import Home from './Home.vue'
import About from './About.vue'
 
const current = ref(Home)
</script>
 
<template>
  <button @click="current = Home">Home</button>
  <button @click="current = About">About</button>
 
  <component :is="current" />
</template>

Diagram:

current

  ├── Home
  │      ↓
  │   <component :is="current">

  └── About

      component berubah

Cocok untuk:

tab
wizard
step
dynamic view

28. 🟡 Provide dan Inject

provide dan inject digunakan untuk mengirimkan data dari komponen leluhur (ancestor/grandparent) langsung ke komponen keturunan (descendant/grandchild) yang berada jauh di dalam component tree tanpa harus meneruskan props secara manual di setiap level (menghindari prop drilling).

Ancestor / Parent Component:

<script setup>
import { provide, ref } from 'vue'
 
// Menyediakan reactive state
const theme = ref('dark')
 
function toggleTheme() {
  theme.value = theme.value === 'dark' ? 'light' : 'dark'
}
 
// provide(key, value)
provide('theme', theme)
provide('toggleTheme', toggleTheme)
</script>
 
<template>
  <ChildComponent />
</template>

Descendant / Grandchild Component:

<script setup>
import { inject } from 'vue'
 
// inject(key, defaultValue)
const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
</script>
 
<template>
  <p>Tema Saat Ini: {{ theme }}</p>
  <button @click="toggleTheme">Ganti Tema</button>
</template>

Diagram:

       Ancestor Component

                │ provide('theme', ref('dark'))

         Child Component  (tidak perlu tahu/meneruskan prop theme)


       Grandchild Component

                │ inject('theme') ──> otomatis reactive!

           UI Terupdate
Reaktivitas pada Provide/Inject
  • Jika data yang di-provide dibungkus dengan ref() atau reactive(), koneksi reaktivitas tetap terjaga. Ketika nilai theme.value di ancestor berubah, komponen anak yang meng-inject data tersebut akan otomatis terupdate.
  • Best Practice: Jika komponen anak perlu mengubah data yang di-inject, sediakan fungsi mutator dari ancestor (seperti toggleTheme di atas) dan jangan mengubah nilai theme.value secara langsung di komponen anak.

Kunci:

props   → komunikasi langsung parent → child
provide → ancestor menyediakan data/fungsi ke level bawah
inject  → descendant mengambil data/fungsi dari ancestor

29. 🔴 Component Instance

Component instance adalah object internal yang mewakili sebuah instance component.

Component


Component Instance
(object component yang sedang berjalan)

Dalam Composition API, biasanya kita tidak perlu mengakses instance secara langsung.

Namun untuk kasus tertentu:

<script setup>
import { getCurrentInstance } from 'vue'
 
const instance = getCurrentInstance()
 
console.log(instance)
</script>

getCurrentInstance() hanya digunakan dalam konteks setup tertentu dan umumnya tidak diperlukan untuk penggunaan Vue sehari-hari.

Untuk expose API dari child ke parent: Child dapat menentukan function/property yang boleh diakses Parent:

<script setup>
function focus() {
  console.log('focus')
}
 
defineExpose({
  focus
})
</script>

Parent dapat menggunakan template ref pada component untuk mengakses API yang diexpose.

Diagram:

Parent

   │ template ref

Child instance

   │ exposed API

focus()

Kunci:

Component Instance → object yang mewakili component
defineExpose()     → menentukan API Child yang boleh diakses Parent
template ref       → Parent mengakses API Child

Catatan: jangan menjadikan akses instance sebagai cara utama komunikasi antar-component. Gunakan props, emits, slots, provide/inject, atau state management sesuai kebutuhan.


30. 🧠 Peta Ingatan Cepat

A. Alur dasar Vue

          STATE


        TEMPLATE


           DOM

            │ user interaction

          EVENT

            │ mengubah

          STATE

            └───────────────┐

                         reactive


                         TEMPLATE

Contoh:

count = 0


{{ count }}


Count: 0

 click

count++


Count: 1

Kunci:

State → Template → DOM
Event → State berubah → Template update

B. State

ref()

nilai reactive

.value di JavaScript

.value diakses langsung di template
 
reactive()

object reactive

property langsung

Contoh:

ref('Budi')

name.value
 
reactive({ name: 'Budi' })

user.name

Kunci:

ref      → nilai tunggal / reference
reactive → object

C. Template

{{ value }}

interpolation

menampilkan nilai
 
:value

v-bind

binding attribute/property
 
@click

v-on

event handler
 
v-if

conditional rendering
 
v-for

list rendering
 
v-model

two-way binding

Kunci:

{{ }}   → tampilkan data
:       → binding
@       → event
v-if    → kondisi
v-for   → perulangan
v-model → two-way binding

D. Computed vs Watch

Butuh MENGHASILKAN NILAI?


       computed
 
Butuh MENJALANKAN SIDE EFFECT?


         watch

Contoh computed:

price + quantity


   computed


    total

Contoh watch:

user berubah


    watch


API / localStorage / logging

Kunci:

computed → menghasilkan nilai
watch    → menjalankan side effect

E. Component Communication

       ┌──────────────┐
       │    Parent    │
       └──────┬───────┘
              │       ▲
        props │       │ emit event
              ▼       │
       ┌──────────────┐
       │    Child     │
       └──────┬───────┘

         slot │

       ┌──────────────┐
       │  UI Content  │
       └──────────────┘

Kunci:

props → parent → child
emit  → child → parent
slot  → parent mengirim template/content ke child

F. Provide / Inject

       Ancestor

          │ provide()

        Parent


        Child


      Descendant

          │ inject()

      gunakan data

Digunakan ketika data perlu digunakan oleh descendant yang jauh, sehingga tidak perlu meneruskan props melalui setiap level (prop drilling). Kunci:

provide → menyediakan data ke bawah
inject  → mengambil data dari ancestor

G. Component Dynamic

          component

       ┌──────┼──────┐
       ▼      ▼      ▼
      Home   About  Profile


        :is="current"


      component yang aktif

Contoh:

<component :is="current" />

Jika:

current = Home

menampilkan Home
 
current = About

menampilkan About

Kunci:

:is → menentukan component yang sedang ditampilkan

31. 📚 Tabel Ringkasan

MateriFungsiKata kunci
PengenalanDasar Vuecomponent, reactive
Membuat ProjectMembuat aplikasiVite, npm
Hello VueComponent pertama<template>
API StyleGaya penulisan VueComposition / Options
TemplateMenulis UI{{ }}
Template AttributesBinding attribute: / v-bind
JS ExpressionEkspresi di template{{ }}
DirectiveInstruksi Vuev-
StateData UIref, reactive
DOM UpdateUpdate UIreactive update
ReactiveData reaktifref, reactive
ComputedNilai turunancomputed()
StyleDynamic styling:class, :style
ConditionalKondisiv-if, v-show
ListPerulanganv-for, :key
EventInteraksi@click
InputForm bindingv-model
WatchersSide effectwatch()
Template RefsAkses DOM/componentref="..."
LifecycleSiklus componentonMounted()
ComponentUI reusable.vue
PropsParent → childdefineProps()
EventChild → parentdefineEmits()
ModelComponent v-modeldefineModel()
FallthroughAttribute ke root$attrs
SlotKirim template<slot>
Dynamic ComponentComponent dinamis<component :is>
ProvideKirim data ke bawahprovide()
InjectAmbil data dari atasinject()
Component InstanceInstance componentgetCurrentInstance()

32. ⚡ Cheat Code Vue 10 Detik

ref() membuat state reactive. reactive() membuat object reactive. computed() membuat nilai turunan. watch() mengawasi perubahan untuk side effect. v-if mengatur kondisi. v-for membuat list. @ menangani event. : melakukan binding. v-model menghubungkan input dengan state. Props mengirim data parent → child. Emit mengirim event child → parent. Slot mengirim template. Provide/Inject mengirim data melewati component tree.


33. 🧭 Urutan Belajar yang Disarankan

1. Pengenalan

2. Membuat Project

3. Hello Vue

4. Template

5. Directive

6. State

7. Reactive

8. Computed

9. Conditional + List

10. Event + Input

11. Watch

12. Component

13. Props + Event

14. Slot

15. Component Model

16. Lifecycle

17. Provide / Inject

18. Dynamic Component

19. Component Instance

34. 🏗️ Mini Project untuk Menggabungkan Konsep

Contoh sederhana Todo App:

<script setup>
import { ref, computed } from 'vue'
 
const newTodo = ref('')
 
const todos = ref([
  { id: 1, text: 'Belajar Vue', done: false },
  { id: 2, text: 'Membuat project', done: true }
])
 
const remaining = computed(() => {
  return todos.value.filter(todo => !todo.done).length
})
 
function addTodo() {
  if (!newTodo.value.trim()) return
 
  todos.value.push({
    id: Date.now(),
    text: newTodo.value,
    done: false
  })
 
  newTodo.value = ''
}
 
function removeTodo(id) {
  todos.value = todos.value.filter(todo => todo.id !== id)
}
</script>
 
<template>
  <h1>Todo App</h1>
 
  <form @submit.prevent="addTodo">
    <input
      v-model="newTodo"
      placeholder="Todo baru..."
    >
 
    <button>Tambah</button>
  </form>
 
  <p>Sisa: {{ remaining }}</p>
 
  <ul>
    <li
      v-for="todo in todos"
      :key="todo.id"
    >
      <input
        type="checkbox"
        v-model="todo.done"
      >
 
      <span :class="{ done: todo.done }">
        {{ todo.text }}
      </span>
 
      <button @click="removeTodo(todo.id)">
        Hapus
      </button>
    </li>
  </ul>
</template>
 
<style>
.done {
  text-decoration: line-through;
}
</style>

Output konsep:

Todo App
 
[ Todo baru...            ] [Tambah]
 
Sisa: 1
 
☐ Belajar Vue       [Hapus]
☑ Membuat project   [Hapus]

Project kecil ini sudah menggabungkan:

ref

 ├── state

 ├── computed

 ├── v-model

 ├── v-for

 ├── :class

 ├── @click

 └── @submit.prevent

Kunci: pahami alur State → Template → Event → State berubah → DOM diperbarui. Setelah itu pelajari Component → Props → Emit → Slot, karena sebagian besar aplikasi Vue tersusun dari pola tersebut.


35. 🔗 Referensi Resmi