Reactive declaration adalah baris di <script> yang diawali $:. Svelte menjalankan ulang baris itu setiap kali variabel yang disebut di dalamnya berubah. Bayangin buku kasir Warung Bu Sri yang dibuat di spreadsheet. Kolom total berisi rumus =harga*porsi. Begitu angka porsi diganti, kolom total ikut berubah sendiri, tanpa ada yang menghitung ulang pakai kalkulator. $: adalah cara menulis rumus seperti itu di komponen Svelte.
🧪 Tebakan cepat: harga Es Teh 5000 dan
porsimulai dari 1. Totalnya dihitung lewat function:function hitungTotal() { return harga * porsi }, lalu$: total = hitungTotal(). Tombol+1 Es Tehdiklik dua kali. Halaman menampilkanTotal: Rp 15.000atau tetapTotal: Rp 5.000? Jawabannya ada di bagian "Yang dilacak cuma yang tertulis".
Nilai yang ikut berubah: $: total = harga * porsi
Di pelajaran Reaktivitas, let total = keranjang.length cuma dihitung sekali. Dengan $:, nilai turunan seperti total selalu ikut sumbernya:
<script>
let harga = 5000
let porsi = 1
$: total = harga * porsi
</script>
<button on:click={() => porsi += 1}>+1 Es Teh</button>
<p>Total: Rp {total.toLocaleString("id-ID")}</p>
| Kejadian | porsi |
total |
Tampil |
|---|---|---|---|
| Awal | 1 | 5000 | Total: Rp 5.000 |
| Klik +1 Es Teh | 2 | 10000 | Total: Rp 10.000 |
| Klik lagi | 3 | 15000 | Total: Rp 15.000 |
$:sebenarnya label JavaScript, fitur lama yang jarang dipakai, jadi kodenya tetap JavaScript yang sah. Svelte memberinya arti baru: "jalankan ulang baris ini kalau yang dia pakai berubah".totalgak perlu ditulislet totaldulu. Svelte membuatkan variabelnya.- Semua baris
$:jalan sekali sebelum tampilan pertama digambar. Setelah itu, baris itu jalan lagi tiap ada variabel yang dia sebut diberi nilai baru, sebelum layar diperbarui. - Variabel yang disebut di baris
$:dan bisa berubah disebut dependency-nya. Di sini dependency-nyaporsi. - Tanpa
$:,let total = harga * porsidihitung sekali dan tetapRp 5.000walaupun porsinya naik.
Yang dilakukan compiler
Svelte 4.2.19 mengubah baris $: tadi jadi kode update:
$self.$.update = () => {
if ($self.$.dirty & /*porsi*/ 1) {
$: $invalidate(1, total = harga * porsi);
}
};
$self.$.updatejalan tiap ada variabel yang baru di-$invalidate, bel dari pelajaran Reaktivitas.dirty & /*porsi*/ 1artinya "apakahporsitermasuk yang berubah?". Kalau ya, total dihitung ulang, dan belnya dibunyikan juga untuktotal, jadi<p>-nya ikut diperbarui.hargagak diperiksa, karena gak pernah diberi nilai baru. Compiler memindahkannya ke luar komponen sebagai nilai tetap.
Latihan: doubled dari count
Latihannya memakai pola yang sama dengan angka yang lebih kecil:
<script>
let count = 2
$: doubled = count * 2
</script>
<p>{count} x 2 = {doubled}</p>
Tampilannya 2 x 2 = 4. Kalau nanti count diberi nilai 7, baris $: jalan lagi dan doubled otomatis jadi 14, tanpa kamu menulis doubled = 14 di mana pun.
$: untuk menjalankan kode
$: juga bisa dipakai di depan perintah biasa, bukan cuma untuk membuat nilai. Bentuk ini disebut reactive statement:
<script>
let porsi = 1
$: console.log("porsi sekarang:", porsi)
$: if (porsi > 5) {
console.log("Pesanan besar!")
}
</script>
Console setelah halaman dibuka lalu porsi naik dua kali:
porsi sekarang: 1
porsi sekarang: 2
porsi sekarang: 3
- Baris pertama jalan di awal, lalu tiap
porsiberubah. $: if (...) { ... }dan blok$: { ... }juga boleh. Semua isinya dijalankan ulang saat dependency-nya berubah.- Pakai bentuk ini untuk efek samping seperti log atau menyimpan ke
localStorage. Untuk menghitung nilai, pakai$: nama = rumus.
Yang dilacak cuma yang tertulis
Ini kode dari tebakan:
<script>
let harga = 5000
let porsi = 1
function hitungTotal() {
return harga * porsi
}
$: total = hitungTotal()
</script>
Setelah dua klik, tampilannya tetap Total: Rp 5.000. Itu jawaban tebakannya. Compiler cuma membaca nama yang tertulis langsung di baris $:, yaitu hitungTotal, dan gak membuka isi function-nya. hitungTotal sendiri gak pernah berubah, jadi baris ini dianggap gak punya dependency. Hasil compile-nya $: $invalidate(1, total = hitungTotal()); tanpa if (dirty ...): jalan sekali saat komponen dibuat, lalu gak pernah lagi.
Cara membetulkannya, sebut dependency-nya di baris $::
function hitungTotal(h, p) {
return h * p
}
$: total = hitungTotal(harga, porsi)
Sekarang porsi tertulis di baris itu, jadi compiler memasang if ($self.$.dirty & /*porsi*/ 1) di sekelilingnya, dan totalnya ikut naik.
📌 Urutan baris $:
Baris $: boleh saling bergantung. Svelte mengurutkannya sendiri berdasarkan siapa butuh siapa, bukan berdasarkan urutan kamu menulis:
$: bayar = total - diskon
$: diskon = total >= 20000 ? 2000 : 0
$: total = harga * porsi
Compiler menyusun ulang jadi total, lalu diskon, lalu bayar. Saat porsi jadi 4: total 20000, diskon 2000, bayar 18000. Kalau urutannya gak disusun ulang, bayar akan dihitung dari total yang belum diperbarui.
Svelte 5: $derived dan $effect
Di Svelte 5 dengan runes, $: dipecah jadi dua rune:
<script>
let harga = 5000
let porsi = $state(1)
let total = $derived(harga * porsi)
$effect(() => {
console.log("porsi sekarang:", porsi)
})
</script>
<button onclick={() => porsi += 1}>+1 Es Teh</button>
<p>Total: Rp {total.toLocaleString("id-ID")}</p>
$derived(...)menggantikan$: nama = rumus.$effect(...)menggantikan$:untuk efek samping.- Svelte 5 mencatat state apa saja yang dibaca saat rumusnya jalan, termasuk yang dibaca di dalam function. Jadi
$derived(hitungTotal())ikut berubah. - Di komponen yang memakai runes,
$:ditolak compiler:`$:` is not allowed in runes mode, use `$derived` or `$effect` instead. - Latihan di sini mencari
$:, jadi tulis gaya Svelte 4.
⚠️ Jebakan yang sering ketemu
let doubled = count * 2tanpa$:. Nilainya dihitung sekali dan gak pernah ikut lagi. Penguji latihan juga mencari$:di kodemu, dan tulisan di dalam komentar gak dihitung.- Dependency yang sembunyi di dalam function.
$: total = hitungTotal()cuma jalan sekali. Kirim nilainya sebagai argumen:hitungTotal(harga, porsi), atau tulis rumusnya langsung. - Mengubah hasilnya, bukan sumbernya.
total = 0di sebuah handler memang mengubah tampilan, tapi begituporsiberubah, baris$:menimpanya lagi. Ubahporsi, biarkantotaldihitung.
TL;DR: $: total = harga * porsi membuat nilai turunan yang dihitung ulang setiap variabel yang tertulis di baris itu diberi nilai baru. Semua $: jalan sekali sebelum render pertama, lalu tiap dependency-nya berubah. $: di depan perintah biasa (console.log, if, blok {}) menjalankan ulang perintah itu. Compiler cuma melihat nama yang tertulis langsung di baris $:, jadi variabel yang dibaca di dalam function gak dilacak; kirim sebagai argumen. Baris $: yang saling bergantung diurutkan otomatis. Latihannya: let count = 2, $: doubled = count * 2, <p>{count} x 2 = {doubled}</p> menampilkan 2 x 2 = 4. Di Svelte 5, pakai $derived untuk nilai dan $effect untuk efek samping.