Skip to content

Bản chất của FE Framework

Hướng dẫn: bài này trả lời câu hỏi cốt lõi — FE framework (Vue, React, Svelte) thực sự làm gì? Nếu chỉ học HTML, CSS, JS basic, không sao, ta bắt đầu từ zero.

Concept nền:

  • HTML: khung xương web, define element (tiêu đề, đoạn, button, ảnh). Xem HTML + CSS Layout
  • JavaScript: cho web "động", sửa content, respond user. Xem JS Deep Guide

DOM là gì?

DOM = Document Object Model.

Khi browser mở web, đọc HTML xong, không hiển thị HTML text trực tiếp, mà convert thành tree structure lưu trong memory. Tree này = DOM tree.

Mỗi node = 1 HTML tag. Quan hệ nest giữa tag → quan hệ parent-child trong DOM tree.

HTML → DOM 树浏览器如何理解你写的 HTML
你写的 HTML 代码
1<html>
2<body>
3<h1>我的购物车</h1>
4<p>共 3 件商品</p>
5<ul>
6<li>耳机</li>
7<li>键盘</li>
8<li>鼠标</li>
9</ul>
10<button>结算</button>
11</body>
12</html>
浏览器解析
浏览器生成的 DOM 树
html
└─body
└─h1"我的购物车"
└─p"共 3 件商品"
└─ul
└─li"耳机"
└─li"键盘"
└─li"鼠标"
└─button"结算"
📄
节点(Node)DOM 树上的每一个方块就是一个节点。每个 HTML 标签(如 <h1><p>)都对应一个节点。
🌳
父子关系标签嵌套在另一个标签里面,在 DOM 树上就是父节点和子节点的关系。<body> 里包含 <h1>,所以 body 是 h1 的父节点。
✏️
DOM 操作JavaScript 可以增加、删除、修改 DOM 树上的节点。修改节点后,浏览器会重新计算布局并重新绘制页面,这就是"DOM 操作"。
关键概念:DOM 是浏览器在内存中维护的一棵树,它和你写的 HTML 一一对应。JavaScript 无法直接修改 HTML 文件,它修改的是这棵 DOM 树——浏览器再根据 DOM 树的变化更新屏幕上的显示。

Sao cần hiểu DOM? Vì JS sửa page = op DOM tree (add/remove/modify node). Framework auto hoá các op DOM này.


0. Mở đầu: "FE framework" là gì?

Framework = bộ code + rule đã viết sẵn, quy định code bạn organize + run thế nào. Viết theo cách của nó, nó lo nhiều việc lặp + phức tạp underlying.

FE framework = chuyên build UI web. Phổ biến: Vue, React, Svelte, Angular.

问题
  • 数据变化时,手动更新 DOM 容易遗漏
  • 页面越复杂,需要同步的地方越多,越容易出 bug
  • 多人协作时,DOM 操作散落各处,维护成本高
根本原因
  • 浏览器不知道"数据"和"界面"的对应关系
  • 原生 DOM API 只提供底层操作,没有"数据变了就更新 UI"的能力
  • 开发者被迫充当"人肉同步器"
框架的解法
  • 建立数据到 UI 的映射关系(UI = f(State))
  • 自动检测数据变化(响应式系统)
  • 自动计算最小 DOM 更新(虚拟 DOM / 编译优化)

1. Vấn đề core: data đổi, UI thế nào?

1.1 "Data" và "UI" là gì?

Mọi web app có 2 thứ cùng tồn tại:

  • Data (State): info trong program. Vd "cart 3 món", "user là Hoàng", "tab thứ 2 đang chọn". Lưu trong JS variable, user không thấy.
  • UI: cái user thấy trên màn hình. Vd "Cart(3)", "Hi Hoàng", tab 2 highlight. Là visual của HTML element.

Data ↔ UI có quan hệ: data "3 món" → UI hiển thị "3". Data thành "4 món" → UI cũng đổi "4".

Vấn đề: ai chịu trách nhiệm "follow đổi"?

数据(JavaScript 变量)
商品数量0
总价¥0
状态正常
✅ 同步
界面(用户看到的)
购物车0 件
总价¥0
状态正常
核心问题:在没有框架的情况下,数据变了,界面不会自动跟着变。你必须自己写代码去更新界面,一旦忘了,用户看到的就是过时的、错误的信息。

1.2 Sao JS variable đổi, UI không auto đổi?

Đây là chỗ newbie hay confused.

Trong JS, variable = vùng memory chứa data. Khi execute count = count + 1, JS engine làm: đổi value count trong memory từ 3 thành 4. Xong, không xảy ra gì khác.

Content trên page (vd <span>3</span> node) lưu trong vùng memory hoàn toàn khác. JS engine sửa variable không biết DOM node đang hiển thị giá trị variable, không có cơ chế tự check.

Bản chất: JS variable + DOM node là 2 vùng memory độc lập, không có cơ chế auto link.

javascript
let count = 3
// Page có DOM node: <span id="counter">3</span>

count = 4
// JS engine: chỉ đổi count trong memory thành 4
// Page <span> vẫn hiển thị "3"

Muốn page đổi thành "4", phải viết thêm code, tìm DOM node, sửa content:

javascript
count = 4
document.getElementById('counter').textContent = count

Nếu page có 5 chỗ hiển thị count, phải viết 5 đoạn. Sót 1 đoạn → chỗ đó vẫn giá trị cũ → user thấy info sai.

1.3 Framework làm gì? 2 bước auto connect

Bước 1: trong template "đăng ký" chỗ nào hiển thị variable

html
<!-- Vue template -->
<span>Cart: {{ count }} món</span>      <!-- Vị trí A -->
<span>Tổng: ¥{{ count * 99 }}</span>    <!-- Vị trí B -->
<span>{{ count > 5 ? 'Nhiều' : 'OK' }}</span>  <!-- Vị trí C -->

Render lần đầu, framework record: A, B, C đều depend count.

Bước 2: framework monitor variable, đổi → check đăng ký → auto update

Framework dùng Proxy của JS để "bọc" variable. Khi sửa variable, Proxy lén thông báo framework "count đổi". Framework check đăng ký, update A, B, C.

Native JS:
  Viết HTML → <span>3</span> (không connect với variable)
  Sửa variable → count = 4 → xong, UI không đổi
  Thêm code → document.getElementById('counter').textContent = 4 → UI update

Vue:
  Viết template → <span>{{ count }}</span> (framework nhớ: chỗ này depend count)
  Sửa variable → count = 4 → Proxy intercept → notify framework → auto update A/B/C
变量修改时发生了什么?原生 JavaScript vs 框架
你写的代码
// 点击按钮时执行
count = count + 1
// 你还要手动写下面这些:
document.getElementById('count')
.textContent = count
document.getElementById('total')
.textContent = count * 99
执行流程
1
JavaScript 修改变量
count 从 0 变成 1
2
找到 DOM 节点
手动调用 document.getElementById()
3
修改 DOM 内容
手动调用 .textContent = 新值
界面结果
购物车0 件
总价¥0
为什么不自动?JavaScript 的变量是"无感知"的。你执行 count = 4 时,JavaScript 引擎只是把内存中 count 的值从 3 改成 4,仅此而已。它不会通知任何人,不会触发任何回调,不会去检查页面上哪里显示了 count。所以界面不会有任何变化——除非你自己写代码去更新 DOM。

1.4 So sánh thực

手动同步 / jQuery 风格
🔴购物车数量已同步
0 件
📋商品列表已同步
(空)
💰总价已同步
¥0
⚠️状态提示已同步
正常
遗漏次数:0
VS
自动同步 / 框架风格
🔴购物车数量已同步
0 件
📋商品列表已同步
(空)
💰总价已同步
¥0
⚠️状态提示已同步
正常
遗漏次数:0
核心思想:前端框架的本质价值在于"自动同步"——你只需修改数据,框架保证所有依赖该数据的 UI 自动更新,不会遗漏。

Đây là lý do tồn tại FE framework: thêm "khi sửa thì auto notify UI update" cho JS variable, loại bug do sync thủ công.


2. Core idea: dùng data mô tả UI

2.1 2 cách viết

Imperative (không framework, jQuery):

javascript
var element = document.getElementById('counter')
element.textContent = '4'
document.getElementById('total').textContent = '¥396'

Declarative (framework):

html
<span>{{ count }}</span>
<span>Tổng: ¥{{ count * 99 }}</span>
<span v-if="count > 5">Quá nhiều!</span>

2.2 Công thức core: UI = f(State)

UI = f(State)

  • State: data app
  • f: render mechanism của framework
  • UI: kết quả user thấy

Data đổi → UI đổi. Dev chỉ care data, không care UI update thế nào.

State(数据)
→ f →
UI(界面)
修改数据(State)
2
渲染结果(UI)
你好,访客!
购物车:2 件商品
总价:¥198
当前主题:浅色
当前 State 快照
{ "username": "(空)", "count": 2, "darkMode": false }
核心思想:你只需要修改数据(State),框架会根据数据自动渲染出对应的界面(UI)。同样的数据永远渲染出同样的界面,这就是 UI = f(State)。

2.3 Sao declarative tốt hơn imperative?

DimImperativeDeclarative
CodeMỗi update viết op cụ thểViết template 1 lần
BugDễ sót updateFramework đảm bảo update đủ
ReadabilityCode lẫn DOM opCode rõ ràng describe UI
MaintainSửa 1 feature → sửa nhiều chỗSửa data logic, UI tự follow

3. Reactivity: framework biết data đổi thế nào?

3.1 "Reactivity" là gì?

JS gốc không có "variable bị sửa tự notify". Framework cần cơ chế "phát hiện" sửa data.

Reactivity = cơ chế này.

3.2 3 cách implement

Cách 1: Proxy intercept (Vue)

Vue dùng Proxy. Proxy có thể execute code khi đọc/sửa property object.

Vue bọc data với Proxy. count = 4 → Proxy intercept → notify Vue → update UI.

Dev không cần làm gì thêm — gán bình thường, Vue tự cảm.

Cách 2: Explicit call (React)

React không dùng Proxy. Phải gọi function chuyên:

javascript
const [count, setCount] = useState(0)

// Không viết count = 4 (React không cảm)
// Phải:
setCount(4)

Chỉ khi gọi setCount(), React mới biết data đổi.

Cách này explicit hơn — mỗi data change đều chủ động báo framework.

Cách 3: Compiler analysis (Svelte)

Svelte có compiler. Trước khi code chạy, compiler phân tích source.

Thấy count += 1, compiler tự insert code "notify UI update" sau dòng đó. Trong code chạy, "notify" đã được compiler sắp xếp trước.

count:0
引擎盖下
1count = 1 → Proxy 的 set 陷阱被触发
2通知依赖收集器:"count 变了"
3找到所有依赖 count 的组件
4自动更新 DOM
核心思想: Vue 通过 Proxy 自动拦截数据读写,开发者无需额外操作——写法最自然。

3.3 So sánh

DimVue (Proxy)React (Explicit)Svelte (Compiler)
Dev writeGán trực tiếp count = 4Phải dùng setCount(4)Gán trực tiếp count = 4
Detect timingRuntime auto interceptDev chủ động notifyCompile time insert code
Runtime overheadProxy có chút overheadsetState scheduling overheadGần như không
DebugTrungData flow rõ, dễCần hiểu compile output
HợpTheo đuổi naturalData flow predictableTheo đuổi perf cực

4. Component: chia UI thành block reusable

4.1 Sao chia?

1 web có nav, sidebar, content, search, avatar, button... Nếu tất cả viết 1 file → file rất dài, khó maintain.

Component = chia UI thành block độc lập, mỗi block quản data + UI + logic riêng.

E-commerce có thể chia:

  • NavBar: nav trên cùng
  • SearchBox: search box
  • ProductCard: card sản phẩm
  • ShoppingCart: cart

Mỗi component độc lập. ProductCard không cần biết code NavBar.

4.2 3 lợi ích

  1. Reuse: ProductCard viết 1 lần, dùng 100 lần với data khác.
  2. Encapsulation: nội bộ data + logic độc lập. Sửa SearchBox không ảnh hưởng ProductCard.
  3. Maintain: lỗi 1 feature, định vị component đó để fix.
组件化拆分一个页面如何拆成多个独立组件
组件树结构
📱App(根组件)
🧭NavBar(导航栏)
🔍SearchBox(搜索框)
🛒CartIcon(购物车图标)
📦ProductCard(商品卡片)×3
📄Footer(页脚)
页面预览
🏠 电商网站🔍 搜索框🛒 购物车(3)
📦
商品 1
¥199
📦
商品 2
¥298
📦
商品 3
¥397
📦 ProductCard(商品卡片)
单个商品的展示卡片。写一次代码,传入不同的商品数据就能复用多次,每次显示不同的商品信息。
数据独立样式隔离 复用 3 次
核心思想:组件化就是把一个大页面拆成多个独立的小块。每个组件管理自己的数据、界面和样式,互不干扰。同一个组件可以在不同地方复用多次,传入不同的数据就会显示不同的内容。

4.3 Component trong code

Vue, component = file .vue có 3 phần:

html
<!-- ProductCard.vue -->
<template>
  <div class="card">
    <h3>{{ name }}</h3>
    <p>Giá: ¥{{ price }}</p>
    <button @click="addToCart">Add Cart</button>
  </div>
</template>

<script setup>
const props = defineProps(['name', 'price'])
function addToCart() { /* ... */ }
</script>

<style scoped>
.card { border: 1px solid #ccc; padding: 16px; }
</style>

Dùng như custom HTML tag:

html
<ProductCard name="Tai nghe" price="299" />
<ProductCard name="Bàn phím" price="599" />
<ProductCard name="Màn hình" price="1999" />

5. Cost DOM op: sao framework cần effort?

5.1 DOM op là gì?

DOM op = JS sửa node trên DOM tree. Sửa text, add element, remove, sửa style.

Op không phức tạp, nhưng browser sau op cần làm nhiều việc:

  1. Recalculate style: node + child cần đổi CSS?
  2. Layout (Reflow): position + size mọi element cần tính lại
  3. Paint: vẽ content lên screen

3 step đều có cost. Code trigger DOM op nhiều → browser execute lặp → page lag.

DOM 操作耗时对比逐个操作 vs 批量操作
逐个操作 DOM
每修改一次数据 → 立刻操作一次真实 DOM → 浏览器每次都要重新布局和绘制
模拟耗时
1修改 → 布局 → 绘制
2修改 → 布局 → 绘制
3修改 → 布局 → 绘制
4修改 → 布局 → 绘制
... 重复 16 次 ...
批量计算后一次性操作
所有修改先在内存中计算好 → 最后只操作一次真实 DOM → 浏览器只需要重新布局和绘制一次
模拟耗时
1内存中计算 20 次变化
2一次性提交 → 布局 → 绘制
核心思想:DOM 操作的真正代价不是"修改值"本身,而是每次修改后浏览器必须执行的"重新布局 + 重新绘制"。减少 DOM 操作次数,就是减少这些昂贵的计算。虚拟 DOM 的作用就是先在内存中算好所有变化,最后一次性提交。

5.2 Framework giải thế nào?

Strategy 1: Virtual DOM + Diff (Vue, React)

Virtual DOM = JS object có structure giống real DOM tree, nhưng chỉ trong memory.

Khi data đổi:

  1. Tạo virtual DOM tree mới
  2. So sánh tree mới + cũ (Diff)
  3. Chỉ apply phần thật sự đổi vào real DOM (Patch)
虚拟 DOM Diff 过程最小化 DOM 更新的核心机制
Old VTree
div.app
h1: 待办清单
ul.list
li: 学习 Vue
li: 写作业
li: 打游戏
Diff Result
div.app
h1: 待办清单
ul.list
li: 学习 Vue
li: 写作业
li: 打游戏
Real DOM
div.app
h1: 待办清单
  • 学习 Vue
  • 写作业
  • 打游戏
7
虚拟 DOM 节点总数
0
需要更新的真实 DOM
节省的 DOM 操作
核心思想: 虚拟 DOM 先在内存中对比新旧两棵树,找出最小差异,然后只更新必要的真实 DOM 节点——避免了大量无效操作。

Strategy 2: Compile-time precise (Svelte)

Svelte không dùng virtual DOM. Compiler phân tích lúc code: "khi count đổi, cần update <span> line 3". Runtime trực tiếp định vị element đó update, skip Diff.

Lý thuyết perf tốt hơn. Phụ thuộc compiler đủ thông minh.


6. Runtime vs Compile-time: trade-off core

6.1 2 stage

  • Compile-time: source code được build tool (Vite, Webpack) xử thành code browser execute được. Xảy ra trên máy bạn, trước user mở web.
  • Runtime: code đã transform execute trong browser user. Logic core framework work ở stage này.

6.2 Work distribution

  • React: đa số runtime. Virtual DOM, Diff, Patch trong browser. Linh hoạt, nhưng phải ship ~40KB runtime.
  • Vue: hybrid. Template optimize lúc compile (mark static node), nhưng UI update qua runtime virtual DOM. ~30KB.
  • Svelte: đa số compile-time. Compiler analyze code, gen DOM op chính xác. Runtime gần như không có code framework. Bundle nhỏ nhất.
框架光谱运行时 ↔ 编译时
更多运行时更多编译时
ReactVue 3Vue VaporSvelteSolid.js
💚Vue 3
混合:编译优化模板 + 运行时虚拟 DOM
运行时工作量
60%
编译时工作量
40%
打包体积中等开发体验★★★★★
趋势: 趋势很明确:框架在不断将工作从运行时移向编译时,目标是同时实现更好的开发体验和更优的运行性能。

6.3 Trend

Framework trend: move work từ runtime sang compile-time. Vì compile time không chiếm tài nguyên user, không ảnh hưởng load.

  • Vue Vapor Mode: skip virtual DOM, compile-time gen DOM op
  • React Compiler: auto optimize re-render
  • Svelte 5 Runes: enhance compile analysis

7. Tổng kết

Vấn đề core FE framework: data đổi → auto, hiệu quả, đáng tin update UI, không phải DOM op thủ công.

Idea chung: UI = f(State).

Tech difference key:

TechNghĩa
ReactivityCách detect data đổi. Vue Proxy, React setState, Svelte compiler
Virtual DOMVue + React dùng JS object mô phỏng DOM tree, Diff để minimal real DOM op
ComponentizationChia UI thành block độc lập reusable
Compile-time optimizationPre-analyze ở build stage, giảm runtime compute. Svelte đi xa nhất

1 câu: FE framework bản chất = take over quá trình sync "data → UI", dev chỉ care data logic.


Glossary

TermPlain
FrameworkCode + rule pre-written, base cho app
DOMTree structure từ HTML, JS op để sửa page
Virtual DOMJS object mô phỏng DOM tree, Diff để minimal update
StateData app
ReactivityKhi data đổi, system auto cảm + update UI
ProxyJS built-in, intercept read/write object. Vue 3 dùng
ComponentBlock UI độc lập + reusable
DeclarativeMô tả "cần gì", framework lo
ImperativeBảo "làm thế nào"
DiffSo sánh 2 virtual DOM tree
PatchApply diff vào real DOM
Compile-timeStage build, trước user mở web
RuntimeStage browser execute code
CompilerConvert source code sang form khác

2026 cho VN dev

  • Vue Vapor: skip virtual DOM, perf gần Svelte
  • React Compiler stable: auto memoization, không cần useMemo/useCallback thủ công
  • Svelte 5 Runes: explicit reactivity, hot
  • Solid.js: fine-grained reactivity từ đầu, best perf
  • Qwik: resumability, instant load (gen HTML + serialize state)
  • VN dev: hiểu nguyên lý → đánh giá tech mới ra (đừng theo hype mù quáng)