Dashboard gudang Siti kini punya dua belas halaman, dan di setiap halaman pola yang sama terulang: state berubah, lalu enam sampai sepuluh elemen harus diikuti — badge jumlah item, tabel baris, ringkasan total. Satu saja yang terlupa, tampilan berbohong. Kode yang menangani itu menyebar ke mana-mana, ditulis tangan, dan tiap penulis punya gayanya sendiri. Budi yang baru bergabung butuh dua hari hanya untuk menemukan di mana satu angka diperbarui.

Framework UI adalah jawaban industri atas pola yang berulang itu. Sebelum membaca peta framework-nya, ada baiknya kita meyakinkan diri: tidak ada yang tersembunyi di dalamnya — semua konsep yang membuat framework bekerja sudah kita pelajari dalam bentuk mentahnya di section Architecture dan DOM.

Masalah yang Diambil Alih Framework #

Tiga pekerjaan muncul di hampir semua aplikasi antarmuka:

  • Sinkronisasi state dan tampilan — ketika data berubah, elemen yang menampilkan data itu harus ikut berubah.
  • Komposisi antarmuka — kartu produk, badge, dan tabel dibangun dari bagian kecil yang dapat dipakai ulang.
  • Pengikatan event — klik tombol dan perubahan input tersambung ke state dengan aturan yang konsisten.

Pekerjaan pertama adalah yang paling berat, dan kitalah yang selama ini mengerjakannya secara manual. Mari kita buktikan dengan dua gaya menangani state yang sama: gaya imperatif yang memperbarui elemen satu per satu, dan gaya deklaratif yang menggambar ulang tampilan dari state. Berkas berikut diuji di lingkungan DOM (jsdom); polanya sama persis di browser:

// ui-dua-gaya.js — dua gaya memperbarui tampilan dari state keranjang yang sama:
// imperatif (selangkah demi selangkah) vs deklaratif (gambar ulang dari state).
// Diuji di lingkungan DOM (jsdom); pola yang sama berlaku di browser.

const keranjang = {
  items: [{ nama: "Kopi Gayo", harga: 45000 }],
};

// GAYA 1: imperatif — setiap perubahan memperbarui elemen satu per satu.
function tambahImperatif(item) {
  keranjang.items.push(item);
  document.querySelector("#jumlah").textContent = String(keranjang.items.length);
  const total = keranjang.items.reduce((s, i) => s + i.harga, 0);
  document.querySelector("#total").textContent = "Rp" + total;
}

// GAYA 2: deklaratif — tampilan adalah fungsi dari state; digambar ulang utuh.
function render(state) {
  const total = state.items.reduce((s, i) => s + i.harga, 0);
  return (
    '<p id="jumlah">' + state.items.length + "</p>" +
    '<p id="total">Rp' + total + "</p>"
  );
}

function tambahDeklaratif(item) {
  keranjang.items.push(item);
  document.querySelector("#panel").innerHTML = render(keranjang);
}

function bacaPanel() {
  const jumlah = document.querySelector("#jumlah").textContent;
  const total = document.querySelector("#total").textContent;
  return `jumlah: ${jumlah} | total: ${total}`;
}

document.body.innerHTML =
  '<div id="panel"><p id="jumlah">1</p><p id="total">Rp45000</p></div>';

tambahImperatif({ nama: "Teh Melati", harga: 60000 });
console.log("Imperatif  ->", bacaPanel());

// Kembalikan keadaan awal, lalu ulangi perubahan yang sama dengan gaya deklaratif.
document.body.innerHTML =
  '<div id="panel"><p id="jumlah">1</p><p id="total">Rp45000</p></div>';
keranjang.items = [{ nama: "Kopi Gayo", harga: 45000 }];

tambahDeklaratif({ nama: "Teh Melati", harga: 60000 });
console.log("Deklaratif ->", bacaPanel());

Output yang Dihasilkan:

text
Imperatif  -> jumlah: 2 | total: Rp105000
Deklaratif -> jumlah: 2 | total: Rp105000

Mari kita telaah bagian-bagian pentingnya:

  1. Baris 10–15 (function tambahImperatif(item) { ... }): satu perubahan data menuntut dua pembaruan tampilan yang ditulis tangan — badge dan total. Tambahkan elemen baru ke panel, dan function ini harus diubah lagi; pembaruan yang terlewat menghasilkan tampilan yang berbohong.
  2. Baris 18–24 (function render(state) { ... }): tampilan dinyatakan sebagai fungsi dari state — satu parameter masuk, satu teks keluar. Tidak ada daftar “elemen yang harus diingat”; semuanya ikut karena seluruh panel digambar ulang dari data.
  3. Baris 26–29 (function tambahDeklaratif(item) { ... }): perubahan state cukup memicu satu tindakan: render lalu tukar isi panel. Keluaran membuktikan kedua gaya berakhir pada tampilan yang identik — bedanya hanya pada siapa yang menjaga sinkronisasi.
  4. Baris 37–38 dan 44–45 (document.body.innerHTML = ...): panel diatur ulang ke keadaan awal antar kedua percobaan, sehingga keluaran kedua benar-benar hasil gaya deklaratif, bukan sisa gaya pertama.

Framework mengotomatiskan gaya kedua. React mempertahankan ide render(state) namun mengganti panel penuh dengan virtual DOM: tampilan baru dibandingkan dengan yang lama, dan hanya bagian yang berbeda yang disentuh. Vue dan Svelte menelusuri ketergantungan data secara halus sehingga pembaruan ditargetkan tanpa membandingkan seluruh tampilan. Svelte bahkan melakukan pekerjaan itu saat kompilasi — komponennya dikompilasi menjadi JavaScript yang sudah memuat instruksi pembaruannya.

Peta Ringkas Empat Framework #

Empat nama mendominasi percakapan framework JavaScript saat ini, dan keempatnya menjawab ketiga masalah di atas dengan filosofi berbeda. Supaya peta ini bukan sekadar kata-kata, komponen kartu produk yang sama kita tulis dalam keempat idiom — semuanya ilustrasi, karena setiap framework punya pipeline build sendiri:

ts Ilustrasi: komponen React
type KartuProdukProps = { nama: string; harga: number };

function KartuProduk({ nama, harga }: KartuProdukProps) {
  return <article>{nama} — Rp{harga}</article>;
}
html Ilustrasi: single-file component Vue
<script setup lang="ts">
defineProps<{ nama: string; harga: number }>();
</script>

<template>
  <article>{{ nama }} — Rp{{ harga }}</article>
</template>
html Ilustrasi: komponen Svelte
<script lang="ts">
  let { nama, harga }: { nama: string; harga: number } = $props();
</script>

<article>{nama} — Rp{harga}</article>
ts Ilustrasi: komponen Angular
@Component({
  selector: "kartu-produk",
  template: "<article>{{ nama }} — Rp{{ harga }}</article>",
})
export class KartuProdukComponent {
  @Input() nama = "";
  @Input() harga = 0;
}
  • React — dibuat di Meta, dirilis 2013. Secara teknis pustaka UI, bukan framework lengkap: routing dan pengambilan data diserahkan ke paket komunitas. Komponen ditulis dengan JSX — markup di dalam JavaScript — dan fleksibilitasnya memicu ekosistem terluas di antara keempatnya.
  • Vue — dibuat Evan You, dirilis 2014. Filosofinya progressive: bisa ditempelkan ke halaman lama bertahap, tanpa rombakan. Komponen berbentuk single-file component yang menyatukan template, logika, dan gaya dalam satu berkas.
  • Svelte — dibuat Rich Harris, dirilis 2016. Filosofinya compiler-first: komponen dikompilasi saat build menjadi JavaScript minimal yang memperbarui DOM langsung, tanpa virtual DOM. Runtime yang dibawa ke browser menjadi kecil.
  • Angular — dikembangkan Google; generasi modernnya dirilis 2016. Framework lengkap: router, forms, dan dependency injection (dibahas di section Architecture) bagian bawaan, dengan CLI yang menyeragamkan struktur proyek. TypeScript adalah bahasa utamanya sejak awal.

Tabel Perbandingan #

AspekReactVueSvelteAngular
Pencipta dan rilisMeta, 2013Evan You, 2014Rich Harris, 2016Google, 2016
Jenispustaka UIframework progresifcompiler frameworkframework lengkap
Bentuk komponenJSX/TSXsingle-file componentsingle-file componentclass dengan decorator
Pembaruan tampilanvirtual DOM dan diffingreaktivitas halusdikompilasi saat buildsignals pada versi modern
TypeScriptdidukung penuh via TSXlang="ts" di SFClang="ts" di SFCbawaan, tidak opsional
Cocok untukSPA fleksibel, ekosistem luasadopsi bertahap, proyek menengahkeluaran kecil, prototipe cepattim besar, standar seragam

Tabel seperti ini menyederhanakan — versi terbaru setiap framework terus menggeser detail teknisnya — tetapi tiga baris terbawah cukup untuk keputusan tingkat pertama. Pencipta dan jenisnya menjelaskan asal ide; baris “Cocok untuk” yang akan kita pakai lagi di chapter penutup section ini.

TypeScript di Ekosistem Framework #

Keempat peta di atas sengaja memuat baris TypeScript, karena pilihan framework dan pilihan tipe kini hampir tidak terpisahkan. React mengetik props lewat type seperti pada ilustrasi pertama, dan berkasnya lazim berekstensi .tsx. Vue dan Svelte menerima blok lang="ts" di dalam komponen — tipe dinyatakan per berkas. Angular menempatkan posisi paling tegas: proyeknya berbahasa TypeScript sejak hari pertama, sehingga pipeline build Angular sudah memuat langkah kompilasi dari chapter Transpilation Overview.

Baris itu juga menjelaskan urutan baca section ini: memahami kompilasi TypeScript membuat pipeline keempat framework tidak lagi asing — setiap framework, dengan bundler masing-masing, pada akhirnya melakukan pekerjaan yang sama. Pertanyaan tersisa bukan “bagaimana ini bekerja”, melainkan “yang mana untuk proyek kita” — dan itulah kerangka keputusan di chapter terakhir.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

ui-dua-gaya.js

Unduh berkas
javascript
// ui-dua-gaya.js — dua gaya memperbarui tampilan dari state keranjang yang sama:
// imperatif (selangkah demi selangkah) vs deklaratif (gambar ulang dari state).
// Diuji di lingkungan DOM (jsdom); pola yang sama berlaku di browser.

const keranjang = {
  items: [{ nama: "Kopi Gayo", harga: 45000 }],
};

// GAYA 1: imperatif — setiap perubahan memperbarui elemen satu per satu.
function tambahImperatif(item) {
  keranjang.items.push(item);
  document.querySelector("#jumlah").textContent = String(keranjang.items.length);
  const total = keranjang.items.reduce((s, i) => s + i.harga, 0);
  document.querySelector("#total").textContent = "Rp" + total;
}

// GAYA 2: deklaratif — tampilan adalah fungsi dari state; digambar ulang utuh.
function render(state) {
  const total = state.items.reduce((s, i) => s + i.harga, 0);
  return (
    '<p id="jumlah">' + state.items.length + "</p>" +
    '<p id="total">Rp' + total + "</p>"
  );
}

function tambahDeklaratif(item) {
  keranjang.items.push(item);
  document.querySelector("#panel").innerHTML = render(keranjang);
}

function bacaPanel() {
  const jumlah = document.querySelector("#jumlah").textContent;
  const total = document.querySelector("#total").textContent;
  return `jumlah: ${jumlah} | total: ${total}`;
}

document.body.innerHTML =
  '<div id="panel"><p id="jumlah">1</p><p id="total">Rp45000</p></div>';

tambahImperatif({ nama: "Teh Melati", harga: 60000 });
console.log("Imperatif  ->", bacaPanel());

// Kembalikan keadaan awal, lalu ulangi perubahan yang sama dengan gaya deklaratif.
document.body.innerHTML =
  '<div id="panel"><p id="jumlah">1</p><p id="total">Rp45000</p></div>';
keranjang.items = [{ nama: "Kopi Gayo", harga: 45000 }];

tambahDeklaratif({ nama: "Teh Melati", harga: 60000 });
console.log("Deklaratif ->", bacaPanel());