⚛️ 2013–2018 SPA 时代

一句话定性

一句话改变了一切:UI = f(state)。前端不再手动操作 DOM,而是声明”UI 应该长什么样”,剩下的交给框架。组件化 + 声明式 + 虚拟 DOM,把前端从”页面制作”升级成”软件工程”。


一、背景:行业现状

Ajax 时代证明了”网页可以是应用”,但用 jQuery 写出来的复杂应用是一场维护噩梦。整个行业都在寻找一种能管理复杂状态的新范式。

  • 2010 年 AngularJS 横空出世,Google 出品,“双向数据绑定 + 依赖注入 + 指令系统”,一度被奉为”前端的未来”。
  • 2013 年 5 月,Facebook 开源 React,带来三个颠覆性概念:虚拟 DOM、单向数据流、组件即函数。初期因 JSX(“HTML 写在 JS 里?亵渎!”)遭群嘲,但很快真香。
  • 2014 年,尤雨溪(Evan You)发布 Vue,取 Angular 的数据绑定 + React 的组件化,主打”渐进式、易上手”。
  • 2016 年,Google 推倒重来发布 Angular-2+(与 AngularJS 完全不兼容),拥抱 TypeScript 和组件化。
  • ES6-ES2015(2015)发布,带来 class、箭头函数、let/const、Promise、模板字符串、原生模块(ES-Modules)——语言本身现代化,为框架提供了趁手的工具。

这是前端框架的”战国时代”,最终格局基本是:React 领先、Vue 紧随、Angular 守住企业级阵地。


二、核心痛点:开发者面临什么

jQuery 解决不了的事

  1. 状态与视图同步是 bug 之源:命令式操作下,改数据要手动改 DOM,改 DOM 要手动改数据,稍有遗漏就不一致。
  2. 没有真正的组件复用:jQuery 插件无法封装”状态+视图+行为”;复制粘贴是常态。
  3. 大型应用没有架构约束:谁都能在任何地方改任何 DOM,代码无法推理(reasoning)。
  4. 频繁操作真实 DOM 性能差:DOM 操作是浏览器里最慢的事之一,手动优化又极易出错。
  5. 路由、状态、数据流全靠拼:做一个”单页应用”要自己拼路由、拼状态管理、拼数据请求。

核心矛盾:应用复杂度已经超过了”人脑能手动同步状态和视图”的上限,需要一种机制让这件事自动化、可推理。


三、代表技术

技术角色专题
AngularJS (2010)先驱:双向绑定的开创者,也是反面教材AngularJS
React (2013)时代定义者:虚拟 DOM + 单向数据流React
Vue (2014)平衡之道:易用性之王,中国崛起Vue
Angular-2+ (2016)企业级全家桶 + TypeScript 原生Angular-2+
JSX”把 UI 当作 JS 表达式”的关键创新React
虚拟 DOM (Virtual DOM)让声明式渲染在性能上可行的核心机制React
Redux (2015)单向数据流的状态管理范式Redux
ES6-ES2015语言现代化,框架的基础设施ES6-ES2015
Webpack (2014→)模块打包器,把组件化工程落地Webpack
Babel让 JSX/ES6 能在老浏览器跑Babel

四、为什么诞生:技术出现的原因

  • React 为什么用虚拟 DOM? Facebook 的痛点是:聊天/通知的未读数等状态,在大型应用里手动同步 DOM 几乎不可能不出 bug。React 的解法极其大胆:别管怎么改 DOM,你只要每次都”重新描述”整个 UI 长什么样(UI = f(state)),我用一个内存中的虚拟 DOM 做 diff,只把变化的部分应用到真实 DOM。这把”如何更新”的责任从开发者手里彻底拿走了。
  • JSX 为什么反而是优点? 当时主流认为”模板和逻辑要分离”。React 反其道而行:UI 本质上就是逻辑的一部分,把它们放在一起(JSX)反而更内聚。这个”离经叛道”的选择,后来被证明是对的。
  • Vue 为什么出现? 尤雨溪在 Google 用 AngularJS,觉得它”太重、太多概念”。他想要”只取最好的部分”:Angular 的数据绑定 + React 的组件化,但更轻、更渐进、更易学。一个人就能从 <script> 标签引入开始用,不需要一上来就上全套工具链。详见 为什么Vue在中国崛起
  • Angular 为什么推倒重来? AngularJS 的双向绑定 + 脏检查(dirty checking)在大型应用里性能崩溃,架构也无法适配组件化潮流。Google 索性 2.0 完全重写,赌上 TypeScript 和 RxJS,做成企业级”全家桶”。代价是抛弃了整个 AngularJS 生态(社区至今心有余悸)。
  • Redux 为什么出现? 组件多了,跨组件共享状态成了难题(数据要层层透传)。受 Flux 架构和 Elm 启发,Redux 提出”单一数据源 + 纯函数 reducer + 不可变更新”,让状态变化可预测、可追溯(时间旅行调试)。详见 Redux

五、解决了什么:具体价值

  • 声明式彻底消灭了”手动同步状态和视图”这个最大 bug 源。开发者只需关心”数据是什么样”,UI 自动跟随。
  • 组件化让 UI 第一次可以真正复用和组合:一个 <Button> 可以封装状态、样式、行为,到处复用。前端开始有了”软件工程”的样子。(把样式也封进组件的需求,直接催生了 CSS-in-JS组件库。)
  • 虚拟 DOM 让”每次重渲染整个 UI”在性能上变得可行(虽然后来证明它并非总是最优,见副作用)。
  • 单向数据流(React/Redux)让大型应用的状态可推理:数据流向单一,出了 bug 能顺着流向追踪。
  • 前端正式从”切图仔/页面仔”独立为一个完整的工程岗位,有了自己的架构、工具链、最佳实践。

六、带来的新问题:技术债与缺陷

SPA 的胜利,埋下了下一个时代的全部痛点

  1. 首屏白屏 + SEO 灾难:SPA 把渲染全搬到客户端,首次加载要先下载一大坨 JS、再执行、再渲染。用户先看到白屏;搜索引擎爬虫看到的是空 <div id="root">这个痛点直接催生了 SSG 的回归。
  2. JS 体积爆炸:框架 + 组件 + 依赖,bundle 越来越大,移动端和弱网下体验崩坏。
  3. 工具链复杂度失控:要用 React/Vue,你得先会 WebpackBabel、loader、plugin、polyfill……”配置一个项目”本身成了专业技能。这是下一个”工程化时代”的核心矛盾来源。
  4. 状态管理过度工程化:Redux 的样板代码(action / reducer / dispatch)被诟病”为了改一个值要写三个文件”,催生了后来的 MobXZustand 等”反样板”方案。
  5. 虚拟 DOM 不是银弹:它有运行时开销,diff 也要成本。这个反思后来直接催生了 Svelte(“编译时干掉虚拟 DOM”)和 编译时优化的整条路线。
  6. AngularJS 留下生态创伤:2.0 的不兼容重写,让大量项目无路可走,也成了”框架不能轻易破坏向后兼容”的著名教训。

七、对下一代技术的影响:因果链

SPA 把渲染全搬到客户端
        │
        ├──► 首屏白屏 + SEO 差 ──► SSR/SSG 回归 ──► Next.js / Nuxt 兴起
        │                                          ──► 进入渲染模式的螺旋(SSR→SSG→ISR→RSC)
        │
        ├──► 工具链复杂到失控 ──► "工程化"成为独立战场 ──► 进入 [[2018-2023 工程化时代]]
        │                          (Webpack 太慢 → Vite;Babel/Terser → esbuild/SWC)
        │
        ├──► 虚拟 DOM 有开销 ──► Svelte(编译时)、Solid(细粒度响应)反思路线
        │
        └──► Redux 样板太多 ──► MobX / Zustand / Recoil 等轻量状态方案
                │
                ↓
        React 自己也在进化:Hooks(2019) → Concurrent → Server Components(RSC)

📊 本时代技术因果图

这张图聚焦本时代内部:谁因为谁的什么痛点而出现

flowchart TD
    imperative["jQuery 命令式痛点<br/>手动同步状态/视图"]
    declare["对声明式的渴望<br/>只描述 UI 长什么样"]
    angularjs["AngularJS 2010<br/>双向绑定 + 脏检查"]
    react["React 2013"]
    vue["Vue 2014"]
    angular2["Angular 2+ 2016"]
    vdom["虚拟 DOM"]
    oneway["单向数据流"]
    comp["组件化"]
    crossstate["跨组件共享状态难<br/>(数据层层透传)"]
    redux["Redux 2015<br/>单一数据源 + reducer"]
    es6["ES6 2015<br/>class / 箭头 / 模块"]
    spa["SPA 单页应用"]

    imperative -->|"不可维护, 无组件复用"| declare
    declare -->|"UI = f(state) + 虚拟 DOM"| react
    declare -->|"渐进式, 只取最好的部分"| vue
    angularjs -->|"双向绑定+脏检查在大应用性能崩溃"| react
    angularjs -->|"概念太重, 想要轻量"| vue
    angularjs -->|"推倒重来, 拥抱 TS"| angular2
    react --> vdom
    react --> oneway
    react --> comp
    comp -->|"组件一多"| crossstate
    crossstate -->|"受 Flux/Elm 启发"| redux
    es6 -->|"语言现代化, 给框架趁手工具"| react

    vdom -->|"声明式渲染在性能上可行"| spa
    react --> spa
    spa -.->|"伏笔: 渲染全搬到客户端, 首屏白屏 + SEO 灾难"| ssr["SSR / 工程化时代"]
    redux -.->|"伏笔: 样板代码太多, 催生 Zustand 等"| light["轻量状态方案"]

    classDef next fill:#eceff1,stroke:#607d8b,stroke-dasharray:4 3;
    class ssr,light next;

本时代的历史地位

SPA 时代是前端的”工业革命”:声明式 + 组件化 + 虚拟 DOM 三大发明,把前端从手工作坊变成了流水线工厂。UI = f(state) 是整个现代前端的”牛顿第一定律”,至今所有主流框架都建立在它之上。但工业化也带来了工业污染——白屏、SEO、巨型 bundle、失控的工具链——这些副作用,正是下一个时代要清理的烂摊子。


🔗 上一篇:2005-2013 Ajax时代 | 下一篇:2018-2023 工程化时代 🔗 相关专题:React | Vue | AngularJS | Angular-2+ | Redux | 为什么React战胜了AngularJS