Membangun Mini Framework JavaScript Bagian 3: Component Reusable, Template Engine, dan Reactive Binding

By | 19 October 2025

Pada bagian sebelumnya, kita sudah berhasil membangun fondasi framework modern dengan Virtual DOM, Router, Lifecycle, dan Store Global. Framework sederhana itu sudah bisa menampilkan halaman berbeda tanpa reload dan mengelola state secara global.

Namun, aplikasi modern tidak hanya butuh data global dan navigasi, tapi juga:

  • Komponen yang dapat digunakan ulang (reusable components),

  • Templating dinamis agar tampilan bisa diatur dengan ekspresi, dan

  • Reactive Binding di JavaScript, supaya tampilan langsung berubah ketika data berubah — tanpa render manual.

Inilah yang akan kita bangun di bagian ketiga ini

Membuat Sistem Komponen Reusable

Konsep komponen memungkinkan kamu membuat potongan UI yang bisa digunakan berulang di berbagai bagian aplikasi.

Contoh Struktur Komponen

const Button = (props) => {
  return h("button", { onclick: props.onClick, class: props.className }, props.label);
};

Komponen di atas bisa dipakai di mana saja:

const App = {
  view: () =>
    h("div", null,
      Button({ label: "Klik Saya", onClick: "alert('Hello!')", className: "btn-primary" }),
      Button({ label: "Tambah", onClick: "Store.setState({count: Store.getState().count + 1})" })
    )
};

Dengan cara ini, kamu bisa menciptakan sistem komponen seperti React. Nantinya, sistem ini akan diperkuat dengan dukungan template binding agar lebih efisien.

Menambahkan Template Engine Sederhana

Untuk menulis tampilan dengan lebih mudah, kita bisa menambahkan template engine berbasis backtick (`) mirip dengan Vue atau Angular.

Fungsi Template Renderer

function renderTemplate(template, state) {
  return template.replace(/\{\{(.*?)\}\}/g, (_, expr) => {
    try {
      with(state) {
        return eval(expr);
      }
    } catch {
      return "";
    }
  });
}

Contoh Penggunaan:

const UserCard = {
  state: { name: "Arvian", role: "Developer" },
  template: `
    <div class="card">
      <h3>{{ name }}</h3>
      <p>Role: {{ role }}</p>
    </div>
  `,
  view(state) {
    const html = renderTemplate(this.template, state);
    const wrapper = document.createElement("div");
    wrapper.innerHTML = html;
    return wrapper.firstElementChild;
  }
};

Sekarang kamu bisa menulis komponen seperti framework besar — cukup dengan {{ data }} binding!

Menambahkan Reactive Binding (Two-Way Data Binding)

Reactive binding berarti ketika data berubah, tampilan langsung diperbarui — dan sebaliknya, saat pengguna mengubah input, datanya ikut berubah.

Kita akan menambahkan fitur seperti v-model di Vue atau ngModel di Angular.

Implementasi Reactive Binding

function bindInputs(root, state, render) {
  root.querySelectorAll("[data-bind]").forEach(el => {
    const key = el.getAttribute("data-bind");
    el.value = state[key] ?? "";

    el.addEventListener("input", e => {
      state[key] = e.target.value;
      render();
    });
  });
}

Contoh Penggunaan Reactive Binding

const FormApp = {
  state: { name: "", message: "" },
  template: `
    <div>
      <input data-bind="name" placeholder="Nama..." />
      <input data-bind="message" placeholder="Pesan..." />
      <p>Halo, {{ name }}! Pesanmu: {{ message }}</p>
    </div>
  `,
  view(state) {
    const container = document.createElement("div");
    container.innerHTML = renderTemplate(this.template, state);
    bindInputs(container, state, () => createApp(FormApp, "root"));
    return container.firstElementChild;
  }
};

Sekarang setiap kali kamu mengetik di input, teks di bawahnya langsung berubah tanpa klik tombol apa pun. Inilah inti Reactive Binding di JavaScript.

Menggabungkan Semuanya: Komponen + Binding + Template

Sekarang kita bisa menggabungkan konsep-komponen di atas menjadi satu aplikasi lengkap.

Store.init({ todos: [] });

const TodoItem = (props) => `
  <li>${props.text} <button onclick="deleteTodo(${props.index})">Hapus</button></li>
`;

function deleteTodo(i) {
  const todos = [...Store.getState().todos];
  todos.splice(i, 1);
  Store.setState({ todos });
}

const TodoApp = {
  state: { newTodo: "" },
  template: `
    <div>
      <h3>To-Do List</h3>
      <input data-bind="newTodo" placeholder="Tugas baru..." />
      <button onclick="addTodo()">Tambah</button>
      <ul>
        {{ todos.map((t, i) => TodoItem({ text: t, index: i })).join('') }}
      </ul>
    </div>
  `,
  view(state) {
    const container = document.createElement("div");
    const todos = Store.getState().todos;
    container.innerHTML = renderTemplate(this.template, { ...state, todos, TodoItem });
    bindInputs(container, this.state, () => createApp(TodoApp, "root"));
    return container.firstElementChild;
  }
};

function addTodo() {
  const todos = [...Store.getState().todos, TodoApp.state.newTodo];
  Store.setState({ todos });
  TodoApp.state.newTodo = "";
  createApp(TodoApp, "root");
}

createApp(TodoApp, "root");

Sekarang kamu punya mini framework yang mendukung:

  • Reusable Components ✅

  • Template Engine {{ binding }}

  • Two-way Data Binding ✅

  • Global Store ✅

  • Router & Lifecycle ✅

Sudah sangat mendekati Vue atau React, tapi lebih ringan dan kamu tahu cara kerjanya dari dasar!

Pengembangan Lebih Lanjut

Setelah fitur ini, kamu bisa kembangkan lebih jauh:

  • Menambahkan sistem event listener otomatis (@click, @input seperti Vue).

  • Menambahkan reactivity berbasis Proxy() agar tidak perlu render manual.

  • Menyusun struktur modular dengan import/export.

  • Membuat DevTools untuk melihat state global secara live.

Dengan begitu, framework ini bisa menjadi playground pribadi untuk belajar konsep frontend modern secara mendalam.

Kesimpulan

Dengan menambahkan Reactive Binding di JavaScript, komponen reusable, dan template dinamis, kamu telah membawa mini framework ini ke level berikutnya. Sekarang framework kamu bukan hanya bisa menampilkan data, tapi juga menyesuaikan tampilan secara otomatis saat state berubah inti dari semua framework frontend modern.