本节摘要:前端从"手搓 DOM"的命令式时代,一步步演进到"声明式 + 组件化"的现代范式。组件化不是某个天才拍脑袋的发明,而是应用变复杂之后被逼出来的必然。读懂这条演进线,你才明白 React、Vue 现在替你扛住的是什么。
本节阅读目标
阅读完本节,你应当能够:
装好 Vue 或 React,敲几行代码页面就出来了,几乎没有"手搓"的体感——这恰恰是问题。框架把大量脏活替你藏了起来,你会误以为那些机制是理所当然的。真到要排查"为什么数据变了页面不动"时,你才后悔没搞清原理。本节用最短的篇幅把演进主线串一遍,作用等同于看地图前先知道"我们是从哪走到这的"。
最早做交互页面,是找元素、改属性、绑事件、再手动刷新。一段典型的旧式代码是这样的:
// 命令式:每一步都要你亲自告诉浏览器怎么改 var count = 0; var btn = document.getElementById('plus'); var label = document.getElementById('count-label'); btn.addEventListener('click', function () { count = count + 1; label.textContent = String(count); // 手动把改好的值写回界面 });
这段代码并不难,难的是应用变大之后。十个互相牵制的状态、几十个需要同时更新的 DOM 节点,你必须在脑子维持一张"哪个数据影响哪个节点"的对应表。漏一个就出 bug,且极难定位——这就是常说的意大利面条式代码的由来。
于是有了早期框架。Backbone 给代码提供了 Model、View、Collection 的分层,让"数据"和"表现"不再糊在一起,但视图更新仍要手动完成,它更像结构约束,不是省心。AngularJS 引入了双向数据绑定和脏检查,靠扫描整个作用域来发现变化,理念向前了一大步,可应用一大会非常费——每触发一次检查就要全量比对。
这两代的共同教训是:分层有用,但"变化到底该由谁通知谁"这个核心问题没有彻底解决。
react、Vue 这一代把答案补齐。关键词有两个,你可以把它理解成表与里:
一条演进时间线能把这四步串起来,图如下。

拿上一节那枚计数按钮做个对照,声明式写法就不再是"每一步都亲自改界面",而是"告诉框架你想表达什么":
function Counter() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> 已点击 {count} 次 </button> ); }
看清楚差别了吗?你不再需要 getElementById 去定位那个标签,也没有 textContent = ... 这一步同步。你只写了"点按钮就把 count 加一,界面上显示 count",剩下"哪个节点要变、怎么变"交给框架。代码量看起来没少多少,可当状态从 1 个涨到几十个、节点从几个涨到上千个,"不用人肉维护那张对应表"的收益会被指数级放大。这就是声明式能统治现代前端的根本原因——它不是写法更优雅,而是把复杂度转移给了框架去统一调度。
把界面拆成可复用单元,换来四件实在的东西:
| 收益 | 怎么体现 |
|---|---|
| 复用 | 一个按钮组件,全站都能用,改一处影响全局 |
| 可维护 | 每个单元自管自,改 A 不牵连 B |
| 并行协作 | 团队可以同时做不同组件,互不阻塞 |
| 关注点内聚 | 组件把结构、逻辑、样式放一起,职责清晰 |
这四条在后面贯穿始终。第 3、4 章的每个机制都在服务"让组件能自包含又简洁地运转"。
需要注意的是,组件化并不是"拆得越细越好",它与"声明式"一样有成本:组件接口要设计、通信要规划、层级要维护。什么时候大力拆、什么时候保持朴素大块,正是这套教程贯穿的"比稿评审"主题——第 2.1 节会给出可操作的三问评审,这里先理解到"组件化是应对复杂度的必然、但需付出接口设计成本"这一层即可。
读演进史最容易犯的错是"只记结论不记过程"。比如记住了"AngularJS 用脏检查所以慢",却不知道它替前端证明了"绑定可以自动做"。下一次你面试时被问"为什么现代框架离不开虚拟 DOM",答案的前半段就藏在 AngularJS 的脏检查里——它是"全量粗暴比对"的参考系。也正是因为每一代都留下了经验与教训,组件化才会演得如此快。把这一节当故事读,比当考点背收获更大。
下一节我们进入两条路线的分岔口:同样高举组件化,React 走声明式、功能式,Vue 走模板式、渐进式,差在哪。