This episode dissects the core architecture of Vue 3: the Proxy-based reactivity system, the template compilation pipeline, the component lifecycle, the anatomy of Single File Components, and how the application instance is mounted and plugins are registered.

Episode 1 explained why Vue exists. Episode 2 answers how Vue works. Before you write component after component, understand the engine behind it: how data becomes reactive, how templates become DOM, and how a component is born and eventually dies.
This episode dissects Vue 3's core architecture from three layers: the Proxy-based reactivity system, the compilation pipeline from template to Virtual DOM, and the component lifecycle. Finally, we'll break down the anatomy of a Single File Component and how an application is mounted along with plugins. This is the most technical episode in the first phase — hang in there, because every concept here will become tangible starting in episode 3.
In Vue 3, reactivity is built on top of JavaScript's Proxy. When you call reactive({...}) or ref(0), Vue wraps the object with a Proxy that intercepts property reads and writes:
import { reactive, effect } from "vue";
const state = reactive({ count: 0 });
effect(() => {
console.log("count sekarang", state.count);
});When the effect runs, it reads state.count, and the Proxy records that the effect depends on that property. When the property changes later, the Proxy tells the effect to run again. This is dependency tracking: every effect knows which data it needs.
The two main reactivity APIs:
ref(0) wraps primitive values; access them through .value.reactive({...}) wraps objects; access them directly without .value.const jumlah = ref(0);
const user = reactive({ nama: "Arman" });
jumlah.value = 1;
user.nama = "Budi";jumlah.value = 1 changes the ref's value, while user.nama = "Budi" changes a property of the reactive object. In templates, the ref's .value is automatically unwrapped, so you only need to write {{ jumlah }}.
Tip
A ref keeps its reactivity even when destructured or passed into functions, because its value is always accessed through .value. A reactive object loses reactivity when destructured — use toRefs to preserve it.
Vue templates aren't read directly by the browser. The flow:
template -> render function -> virtual dom -> patch -> dom nyataThis keeps development declarative (you write templates) while performance is preserved because only the differences touch the DOM.
In a Vite project, template compilation happens at build time through the Vue plugin, so the bundle sent to the browser contains render functions — not raw templates. This explains why Vue applications don't need a compiler in production: compilation is already done at build time.
Every component passes through the following phases:
import { onMounted, onUpdated, onUnmounted } from "vue";
onMounted(() => {
console.log("komponen dipasang ke DOM");
});
onUpdated(() => {
console.log("komponen diperbarui");
});
onUnmounted(() => {
console.log("komponen dilepas");
});The order: setup runs first, then the component is mounted (onMounted), updated whenever data changes (onUpdated), and finally unmounted (onUnmounted). In onMounted you typically fetch data or start an interval; in onUnmounted you clean them up.
A Single File Component (SFC) packages the structure, behavior, and appearance of one component in a single file:
<script setup>
import { ref } from "vue";
const nama = ref("Dunia");
</script>
<template>
<h1>Halo, {{ nama }}</h1>
</template>
<style scoped>
h1 {
color: #42b883;
}
</style>Three main blocks: <script setup> for logic with the Composition API, <template> for markup, and <style scoped> for CSS that only applies to this component. scoped works by adding a unique attribute to every element at compile time, preventing styles from colliding between components.
A component's interface consists of three directions of communication:
<script setup>
const props = defineProps({ judul: String });
const emit = defineEmits(["klik"]);
</script>
<template>
<button @click="emit('klik')">{{ props.judul }}</button>
</template>defineProps and defineEmits are compiler macros — you only declare them, and the compiler extracts them automatically. We'll dig into the full details of component communication in episode 6.
A Vue application starts from a single root component mounted onto a DOM element:
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const app = createApp(App);
app.use(createPinia());
app.mount("#app");createApp(App) creates the application instance, app.use(createPinia()) registers the Pinia plugin, and app.mount("#app") mounts the application onto the element with id app. Plugins are the mechanism for adding global capabilities — like routing, state management, or i18n — without changing the application code.
Episode 2 dissected Vue's engine from the inside: the Proxy-based reactivity system with dependency tracking, the template pipeline that compiles into a Virtual DOM, the well-defined component lifecycle, and the anatomy of an SFC with <script setup>, a template, and scoped styles.
Key takeaways:
Proxy and dependency tracking.ref for primitive values, reactive for objects.createApp; plugins are registered with app.use.In the next episode 3, we'll start a real Vue project — the src/components and src/views folder structure, how the dev server and hot module replacement work, plus ESLint, Prettier, and lint-staged configuration to keep code quality high from day one.