图谱 · 主题分线图
图谱 · 主题分线图
关系图谱-技术因果链 是一张把所有线缠在一起的”总网”,信息密但难以单独追一条脉络。 这篇把那张总网拆成 9 条独立的专线,每条线只讲一件事:抽象怎么上移、模块化怎么收敛、渲染怎么螺旋、工具链怎么提速、状态管理怎么摆动、CSS 布局怎么还债、UI 组件控制权怎么回归、算力怎么向用户靠近、前端怎么扩张领土。 单线更聚焦——你可以一次只盯着一个矛盾看它三十年的演化。
- 每张图只画一条主线,横向(LR)或纵向(TD)就是时间方向。
- 边标签是这条线的”核心动作”:这一代少关心了哪一层、或为了对抗什么副作用而生。
- 五条线合起来,就是 关系图谱-技术因果链 的总网。
一、抽象上移线 · 让开发者少关心一层
flowchart LR
dom["手写 DOM<br/>命令式"]
jquery["jQuery 2006<br/>命令式封装"]
framework["React / Vue 2013+<br/>声明式"]
svelte["Svelte<br/>编译器"]
ai["AI Coding 2023+<br/>自然语言"]
dom -->|"少关心: 浏览器差异 (兼容性)"| jquery
jquery -->|"少关心: 怎么操作 DOM (改为描述 UI=f(state))"| framework
framework -->|"少关心: 运行时框架本身 (编译期干掉)"| svelte
svelte -->|"少关心: 怎么写代码 (改为描述意图)"| ai
classDef hi fill:#e3f2fd,stroke:#1976d2;
class dom,jquery,framework,svelte,ai hi;
每一代做的其实是同一件事:把上一代还要操心的那一层,藏到地平线以下。 jQuery 让你不必再关心 IE 和 Netscape 的私有 API;React 让你不必再关心”怎么把 state 同步到 DOM”;Svelte 让你连”运行时框架要打包进产物”都不必关心;AI 让你连”怎么把意图翻译成代码”都可以交出去。 抽象每上移一层,生产力提升一个量级,但理解力也同步下沉一层——这正是 演进逻辑-五个永恒矛盾 里”抽象 vs 掌控”那条矛盾。详见 前端框架演进史 与深度专题 AI编程会让前端框架收敛吗。
二、模块化线 · 社区先行、标准追认的潮汐
flowchart LR
script["script 标签<br/>全局污染"]
cjs["CommonJS / AMD<br/>社区方言"]
browserify["Browserify 2011<br/>搬进浏览器"]
webpack["Webpack 2014<br/>万物皆模块"]
vite["Vite 2020<br/>开发期免打包"]
esm["ES Modules<br/>语言标准"]
script -->|"全局变量冲突, 无依赖管理"| cjs
cjs -->|"Node 模块能否搬到浏览器?"| browserify
browserify -->|"打包思路被发扬光大"| webpack
webpack -->|"冷启动/HMR 太慢"| vite
esm -.->|"标准最终追认, 浏览器原生支持"| vite
cjs -.->|"标准缺位逼出社区方言"| esm
classDef std fill:#fff3e0,stroke:#e65100;
class esm,vite std;
浏览器迟迟不给原生模块系统,社区只能自己造:CommonJS、AMD、UMD 一堆方言并存。Browserify 和 Webpack 用”打包”把方言在构建期抹平。直到 ESM 成为语言标准、浏览器原生支持,Vite 才得以借原生 ESM 实现”开发期免打包”——反过来淘汰了打包器的一部分存在理由。
注意图里 esm -.-> vite 是虚线:它表示”标准追认”——标准不是发明者,而是把社区已经跑通的实践事后扶正。这就是前端反复出现的”社区先行、标准追认”节奏。详见 ES-Modules、Webpack、Vite 与 为什么Vite能取代Webpack。
三、渲染模式螺旋线 · 否定之否定
flowchart TD
mpa["MPA<br/>服务端渲染, 整页刷新"]
spa["SPA<br/>客户端渲染, 白屏/SEO 差"]
ssr["SSR<br/>渲染回归服务端"]
ssg["SSG<br/>构建期预渲染"]
isr["ISR<br/>增量再生成"]
rsc["RSC<br/>服务端组件"]
mpa -->|"交互弱, 体验割裂 → 搬到客户端"| spa
spa -->|"首屏白屏 + SEO 灾难 → 搬回服务端"| ssr
ssr -->|"内容型站点无需每次实时渲染"| ssg
ssg -->|"全量构建太慢, 要按需更新"| isr
isr -->|"组件级别地划分服务端/客户端"| rsc
classDef back fill:#e8f5e9,stroke:#2e7d32;
class mpa,ssr,ssg,isr,rsc back;
渲染的位置在”服务端 ⇄ 客户端”之间来回摆:前端先把渲染从服务端搬到客户端(SPA)以换取交互,却付出了白屏和 SEO 的代价;于是又把渲染搬回服务端(SSR/SSG/ISR/RSC)。 但这不是简单的回退——回到服务端时,前端带着组件化、水合(hydration)、流式渲染、服务端组件这些 SPA 时代练就的新能力回去了。这就是”否定之否定”:形式回到起点,内涵已完全不同。详见 渲染模式演进史。
四、工具链提速线 · 用更快的语言重写一切
flowchart LR
subgraph BUILD["打包/编译器"]
webpack["Webpack<br/>功能全但慢 (JS 写)"]
esbuild["esbuild / SWC<br/>Go / Rust 重写"]
vite["Vite<br/>原生 ESM 按需编译"]
unified["Turbopack / Rolldown<br/>Rust 统一底座"]
webpack -->|"JS 单线程, 冷启动慢"| esbuild
esbuild -->|"开发期用它做转译, 极速"| vite
vite -->|"dev/prod 双引擎不一致, 想统一"| unified
end
subgraph PKG["包管理器"]
npm["npm<br/>慢/黑洞"]
yarn["Yarn<br/>锁文件/并行"]
pnpm["pnpm<br/>硬链接/治幽灵依赖"]
npm -->|"无锁文件, 安装慢"| yarn
yarn -->|"node_modules 黑洞 + 幽灵依赖"| pnpm
end
subgraph RT["运行时"]
node["Node.js<br/>事实标准"]
deno["Deno<br/>安全/TS/ESM 优先"]
bun["Bun<br/>一体化 + Zig 极速"]
node -->|"历史包袱: 安全/模块/TS"| deno
node -->|"想要更快的一体化运行时"| bun
end
classDef fast fill:#fce4ec,stroke:#c2185b;
class esbuild,vite,unified,pnpm,deno,bun fast;
2020 年后,框架之争降温,竞争焦点全面转向开发体验(DX)与构建速度。主旋律只有一句话:用更快的语言(Go / Rust / Zig)重写 JS 工具链。 三条并行的赛道殊途同归——打包器(Webpack → esbuild/SWC → Vite → Turbopack/Rolldown)、包管理器(npm → Yarn → pnpm)、运行时(Node → Deno/Bun)都在比谁更快、谁更省、谁更一体化。详见 Vite、Turbopack、npm-Yarn-pnpm-包管理、Deno、Bun 与 为什么Vite能取代Webpack、为什么pnpm解决了依赖问题。
五、状态管理钟摆线 · 可预测性 vs 样板代码
flowchart LR
manual["手动 DOM 状态<br/>状态散落各处"]
flux["Flux 2014<br/>单向数据流思想"]
redux["Redux 2015<br/>可预测但样板多"]
mobx["MobX<br/>响应式, 自动追踪"]
zustand["Zustand 2021<br/>极简 Hook"]
pinia["Pinia<br/>Vue 官方, 轻量"]
manual -->|"状态无单一来源, 难追踪"| flux
flux -->|"落地为单一 Store + 纯函数 reducer"| redux
redux -->|"样板代码太多 → 摆向响应式"| mobx
redux -->|"样板代码太多 → 摆向极简"| zustand
redux -.->|"在 Vue 生态摆向轻量"| pinia
mobx -.->|"钟摆: 自动 vs 可预测之间反复"| redux
classDef swing fill:#ede7f6,stroke:#5e35b1;
class redux,mobx,zustand,pinia swing;
状态管理的演化不是单调向前,而是一只钟摆,在两极之间摆动:
- 可预测性那一极:Flux/Redux 用”单向数据流 + 不可变 + 纯函数 reducer”换来时间旅行、可追踪、易调试,代价是大量样板代码。
- 省事/响应式那一极:MobX 用自动依赖追踪让你”改对象就更新”,Zustand/Pinia 用极简 API 把样板砍到最低,代价是可预测性下降、调试链路变隐式。 没有哪一极是终点——每当一极的代价积累到痛点,社区就摆向另一极。这正是 演进逻辑-五个永恒矛盾 的具体演绎。详见 Redux、MobX、Zustand、Vuex-Pinia。
六、CSS 布局线 · 还了二十年的债
flowchart LR
table["table 布局<br/>用表格凑版面"]
float["float + clearfix<br/>浮动 + 清除 hack"]
inline["inline-block<br/>空白符 bug"]
flex["Flexbox 2015<br/>一维布局"]
grid["Grid 2017<br/>二维布局"]
cq["Container Queries 2023<br/>按容器响应"]
table -->|"语义错位, 嵌套地狱"| float
float -->|"脱离文档流, 要清浮动 hack"| inline
inline -->|"无法垂直居中, 空白符难缠"| flex
flex -->|"只能一维, 复杂网格仍吃力"| grid
grid -->|"只能按视口响应, 组件不自洽"| cq
seed["IE 盒模型之争<br/>同一段 CSS 两种宽度"]
seed -.->|"兼容性原罪贯穿始终"| float
classDef native fill:#e0f2f1,stroke:#00897b;
classDef pain fill:#fff3e0,stroke:#e65100;
class flex,grid,cq native;
class seed pain;
CSS 诞生时只为文档样式,根本没想过要承担”应用级布局”。于是前端被迫用错工具凑合了十几年:先用 table(本是表格数据)拼版面,再用 float(本是文字绕排图片)做多栏 + 满地 clearfix hack,再用 inline-block 又踩进空白符的坑。
直到 Flexbox(2015)和 Grid(2017) 这两个为布局而生的原生能力普及,这场长达二十年的”凑合”才真正结束;**Container Queries(2023)**进一步让组件能按自己所在容器(而非视口)响应,补齐了组件化时代的最后一块布局拼图。
注意虚线那条:IE盒模型之争——同一段 width 在 IE 和标准里算出两种宽度——是这条线的”原罪”,兼容性焦虑几乎贯穿了 float 时代的始终。详见 布局演进史、CSS演进史。
七、UI 组件控制权线 · 封装度的钟摆,控制权的回归
flowchart LR
bootstrap["Bootstrap 2011<br/>只给样式 (CSS 框架)"]
libs["AntD / MUI / Element 2015+<br/>给完整组件"]
headless["Headless / Radix 2020+<br/>只给行为 + a11y"]
shadcn["shadcn/ui 2023<br/>连依赖都不要"]
bootstrap -->|"一眼同质化, 难深度定制"| libs
libs -->|"样式难覆盖, a11y 又极难自己写"| headless
headless -->|"行为归库, 样式归你 (配 Tailwind)"| shadcn
tw["原子化 CSS / Tailwind"]
tw -.->|"为 Headless/shadcn 提供样式层"| headless
ai["AI 时代: 改本地源码比改 node_modules 顺手"]
shadcn -.->|"源码归你, 利于 AI 直接改"| ai
classDef ctrl fill:#ede7f6,stroke:#5e35b1;
classDef ext fill:#eceff1,stroke:#607d8b,stroke-dasharray:4 3;
class headless,shadcn ctrl;
class ai ext;
UI 组件方案的演化,是控制权一步步从库手里回到开发者手里的过程:
- Bootstrap:只给样式,你套个 class 就有像样的页面——快,但”一眼 Bootstrap 味”,难改。
- AntD / MUI / Element:给你封装好的完整组件(含交互+样式),中后台开箱即用——但样式覆盖痛苦、与框架强绑定、包还大。
- Headless(Radix / Headless UI):只给最难自己写的那部分——行为、状态、无障碍(a11y),样式完全交还给你(通常配 Tailwind)。封装度下降,控制权上升。
- shadcn/ui:连”依赖”这个概念都掀翻了——用 CLI 把组件源码复制进你的项目,你彻底拥有它。这在 AI 时代尤其契合:让 AI 改你项目里的本地源码,远比改
node_modules里的第三方依赖顺手。 这条线和「状态管理钟摆线」「抽象上移线」共享同一种张力:省事 vs 可控。详见 UI组件库演进史、Bootstrap、Headless-UI、shadcn-ui。
八、部署托管线 · 算力一路向用户靠近
flowchart LR
server["自建服务器<br/>中心化机房"]
cdn["CDN<br/>边缘缓存静态资源"]
jam["Jamstack 2015<br/>预构建静态 + API"]
faas["Serverless / FaaS 2014<br/>函数即服务"]
edge["Edge 2017+<br/>边缘运行时, 贴近用户"]
server -->|"静态资源延迟高 → 推到边缘缓存"| cdn
cdn -->|"纯静态 + 客户端调 API"| jam
jam -->|"动态逻辑也想免运维 → 函数化"| faas
faas -->|"冷启动 + 离用户远 → 推到边缘节点"| edge
classDef near fill:#e1f5fe,stroke:#0277bd;
class cdn,jam,faas,edge near;
这条线只讲一件事:把”代码在哪里跑、内容从哪里来”一步步推到离用户更近的地方。 中心化服务器延迟高 → CDN 把静态资源缓存到边缘 → Jamstack 把页面预构建成静态托管(动态部分交给客户端调 API)→ Serverless 让动态逻辑也免运维、按需伸缩 → Edge 把代码本身推到全球边缘节点,毫秒级冷启动、贴着用户跑。 它和「渲染模式螺旋线」是一对孪生兄弟:渲染回到服务端(SSR/RSC),但这次服务端被搬到了边缘——动态内容也能像静态一样快。代价是 Edge 运行时只是 Web 标准子集(这恰好契合 Deno/Bun 的设计)。详见 部署与托管演进史、Edge边缘运行时、Jamstack 与 渲染模式演进史。
九、领土扩张线 · 一套 JS/TS 征服所有平台
flowchart TD
web["浏览器<br/>前端的起点"]
mobile["React Native 2015<br/>移动端"]
desktop["Electron 2013<br/>桌面端"]
mini["小程序 2017<br/>超级 App 内"]
fullstack["全栈: Node → API routes<br/>→ BFF → tRPC"]
rsc["RSC<br/>前后端边界内化进组件树"]
web -->|"复用 Web 技能写原生 App"| mobile
web -->|"Chromium+Node 写桌面应用"| desktop
web -->|"超级 App 入口 + 即用即走 (中国)"| mini
web -->|"Node 让前端能写后端"| fullstack
fullstack -->|"前后端边界从网络请求变成组件树里一条线"| rsc
classDef expand fill:#f1f8e9,stroke:#558b2f;
class mobile,desktop,mini,fullstack,rsc expand;
前端从浏览器出发,用同一套 JS/TS 技能栈不断吞噬相邻领域:向移动端扩张(React Native)、向桌面扩张(Electron)、在中国向超级 App 内扩张(小程序),以及向服务端扩张(Node 全栈 → API routes → BFF → tRPC 端到端类型安全)。 这条线的终点是 RSC:前后端的边界不再是”一次网络请求”,而是组件树里的一条线——哪段在服务端跑、哪段在客户端跑,由组件粒度决定。扩张的二阶代价是复杂度守恒:边界模糊带来职责膨胀,前端工程师要懂的越来越多。详见 跨端与全栈演进史、React-Native与移动跨端、小程序生态、Electron与桌面端、全栈框架与BFF、RSC与前后端边界模糊。
把总网拆成五条线后会发现,它们其实是同一种张力在不同维度的投影:每一代都在”更省事 / 更快 / 更省心”和”更可控 / 更可预测 / 更透明”之间寻找新的平衡点,平衡一旦被打破,下一代就诞生了。
相关:MOC-前端技术演化史 | 关系图谱-技术因果链 | 图谱-竞争与替代关系 | 图谱-框架血缘谱系 | 时间线总览 | 演进逻辑-五个永恒矛盾 时代:1995-2005 浏览器时代 | 2005-2013 Ajax时代 | 2013-2018 SPA时代 | 2018-2023 工程化时代 | 2023-未来 AI时代 关键技术:jQuery | React | Vue | Svelte | Vite | Webpack | 渲染模式演进史 | Redux