前端演进史:当你没留意时,前端发生了什么

前端演进史:当你没留意时,前端发生了什么

本文是对 The Descent --- What Happened to the Frontend While You Weren't Watching 的中文翻译。


导读

如果你曾是一名前端开发者,后来转向了其他方向------后端、架构、管理,或者只是暂时离开了这个领域------再回来看时,大概会被眼前的工具链吓一跳:Vite、esbuild、SSR、RSC、Hydration、Islands、Tailwind、Ant Design......每一个名词背后都是一整段演进史。

这篇文章的精妙之处在于,它没有把这些工具当成一份需要死记硬背的清单,而是从 2008 年那个拖 FTP 上传 index.html 的年代讲起,沿着每一层"痛点 → 修复 → 新痛点"的因果链一路向下挖掘,直到触底------你会发现 2026 年的前沿,和当年那个简单的 HTML 文件惊人地相似。

原文以"下探"(The Descent)为喻,将前端近二十年的演进分为八个深度层级(0--44 米),每层对应一个核心问题的诞生与解决。全文围绕一个 <button> 元素的变迁展开,从 22 字节的手写标签,到被 React 组件包裹,再到被 TypeScript 类型约束、被 Tailwind 样式化、被服务端渲染------一个按钮的命运,浓缩了整个前端工程的进化史。


序章

你上一次写前端的时候,代码大概长这样------

服务端渲染 · 2009 年

php 复制代码
<?php foreach ($cart as $item): ?>
  <li>
    <?= $item->name ?>
    <button>Buy</button>
  </li>
<?php endforeach; ?>

或者纯手写

html 复制代码
<!-- 手写样式,没有构建步骤 -->
<button class="buy">Buy</button>

<style>
  .buy {
    padding: 4px 10px;
    border: 1px solid #999;
  }
</style>

无论哪种方式:0 个构建步骤、0 个依赖 ,在所有浏览器里都能跑,而且永远能跑。

然后你移开了视线。这到底发生了什么?

近二十年的光阴,一路下潜四十四米的构建工具链,外加一个被折腾得面目全非的 <button>------这就是全部的故事。给我二十分钟,帮你把落下的功课补回来。

2008 年,你保存一个 index.html,拖进 FTP 客户端,看着进度条一点一点爬到头。传输完毕,网站就上线了。它在 IE、Firefox 和那个新出的叫 Chrome 的浏览器里都能正常工作。你不需要跑"构建",不需要安装 1400 个包。你写标记、写样式,浏览器忠实执行你的指令。

你曾经很擅长这件事。然后,在 iPhone 发布到现在的某个时间点,你眨了一下眼------整个学科就在你忙着交付产品的时候,从地基开始重建了自己。如今一个入门教程开篇就甩出十六个你没听过的工具,其中一半用日语单词"快"命名,敲下的第一条命令下载的代码量比阿波罗登月导航计算机还多------就为了渲染一个联系表单。

好消息是------这也是整篇文章的核心论点:这些工具的出现都不是随意的。 你接下来遇到的每一个工具,都是真实痛点催生的疤痕组织。有人遇到了一个真正的问题,做了一个修复方案,而修复方案本身又制造了下一个问题,下一个问题又催生了新的修复方案。二十年来一系列合理的步骤叠加在一起,就变成了你如今仰望的那座宏伟而令人疲惫的疯狂大教堂。

所以我们不背清单。我们按顺序往下挖,让每个工具恰好在能证明它存在价值的痛点出现时登场。我们会一路追踪一个样本------一个不起眼的 <button>------看看行业对它做了什么。当我们触到基岩的时候,你会笑出来:因为 2026 年的前沿,看起来和你当年通过 FTP 上传的那个文件惊人地相似。

每一个工具都是真实痛点催生的疤痕组织。顺着痛点走,地图自己就画出来了。


第一层 --- 4 米 · 2006--2010 年

痒处初现

"我只是想不刷新整个页面就能改变页面的某一部分。"

第一个痛点小而合理。你希望页面的某一部分发生变化------展开菜单、校验表单、加载某个区块------而不要全页刷新时那一下白屏闪烁。浏览器可以通过一个叫 XMLHttpRequest 的粗糙接口和一堆繁琐的 DOM 方法做到这件事,但这些方法在每个浏览器里的行为都不一样。

于是那个时代选择了 jQuery,它确实非常棒。$("#cart").load(...) 一写,浏览器间的差异就消融了。AJAX(不刷新页面就能获取数据)成为主流。有那么几年黄金时光,这就够了。

但应用越来越有野心,一个更深的痛点在底层浮现了。当你的数据同时存在于 JavaScript 变量里 屏幕上时,你自己就是那台同步机器。改了价格?最好记得手动更新购物车总价、头部徽标、结账按钮和那个小小的摘要框。漏掉一个,UI 就对用户撒了谎。这种在不断膨胀的应用中手动操作 DOM 的做法,就是"原罪"------从这层往后的所有东西,都是在试图赎这个罪。

产物 --- jQuery 仍然运行着大量网站;但没人再用它开新项目了。


第二层 --- 9 米 · 2010--2015 年

框架登场

"别再让我手动把屏幕和数据同步了。"

修复这个同步痛点的是一个真正的大想法:声明式 UI。与其写更新页面的步骤 ("找到徽标,设置它的文字"),不如描述页面在给定数据下应该是什么样子 ,然后让框架去算出步骤。你不再碰 DOM 了。你改数据,屏幕跟着变。这就是你不断看到的 reactive(响应式)这个词的含义。

构建的基本单位变成了组件 :一个自包含的 UI 块(包含标记、行为和一点状态),像乐高一样拼装。2013 年出自 Facebook 的 React 赢得了这个时代,它把组件和 JSX(一种写在 JavaScript 里面的、看起来像 HTML 的语法,至今仍让人不安)以及一个叫虚拟 DOM 的巧妙把戏配在了一起:在内存里保留页面的轻量副本,每次变更时做 diff,只触碰真正发生变化的 DOM 节点。

然后百花齐放。Vue 让它变得平易近人。Angular(成熟版重写,不是 2010 年那个已死的 "AngularJS")带来了企业级结构和 TypeScript。Svelte 质问为什么要把框架送到浏览器里,然后把自己编译没了Solid 保留了 JSX 但抛弃了虚拟 DOM,做手术刀式的精准更新。它们现在都在收敛向同一个东西------信号(signal):一个知道自己被谁依赖、只更新那些依赖者的值,不多不少。

样本 01 --- 按钮变成了组件 · 2013 年

jsx 复制代码
// 这已经不是标记了。这是 JavaScript
// 在假装自己是标记。

function Buy() {
  return (
    <button onClick={addToCart}>
      Buy
    </button>
  )
}

按钮现在有了 state (状态)、props (属性)和父级组件树。没有浏览器里的框架运行时,它就无法存在。要看到它,你很快就需要一个构建步骤------继续往下挖。

  • 源码体积:~140 字节
  • 附带:React 运行时 ~45 KB
  • 构建步骤:1 个(还在增加)

旧时代的逃生舱 --- 不是所有人都走了这条路。一个固执且不断壮大的阵营------htmxAlpine.js、Rails 的 Hotwire------认为原罪在于离开了服务器,于是把"通过网线传 HTML"重新带了回来。如果这一层让你感到思乡,那个阵营就是为你建的,我们到基岩时还会回到它。


第三层 --- 15 米 · 2012--2018 年

构建步骤

"为什么我没法直接打开文件了?"

这是你最怨恨的一层,所以我们坦诚地聊聊它为什么存在。一旦 JavaScript 开始构建整个 页面,两个事实就撞在了一起。第一,这门语言大半辈子都没有真正的跨文件代码拆分机制------没有 import------于是社区发明了相互竞争的模块系统(Node 里 CommonJS 的 require,后来又有官方的 ES Modules 的 import),至今也没有完全统一。第二,你想写现代语法和 JSX,而老浏览器看不懂。

于是翻译层出现了。Babel 把明天的 JavaScript(和 JSX)转译成昨天的版本,让任何浏览器都能跑。打包器webpack 是王者)把你数百个小文件缝合成几个大文件,因为在旧的 HTTP 协议下,数百个请求太慢了。在这个过程中它还做代码压缩(去掉空白)、摇树优化(丢弃你从没用到过的代码)、把结果切分成块、以及生成 source map 让你仍然能调试原始代码。这一大堆活儿的总和,就是"构建"。

代价呢?node_modules:一个存放你的依赖、依赖的依赖、以及依赖的依赖的依赖的文件夹。一个空白入门项目动辄落下二十五万个文件。它是宇宙中最重的物体,此刻就坐在你的项目文件夹里。

你那个 22 字节的按钮,现在装在一个 2MB 的包里到达,由一个你没写过也看不懂的配置文件组装而成。


第四层 --- 21 米 · 2018--2024 年

工具军备竞赛

"好吧,有构建步骤。但能不能让它别花九十秒?"

构建能用,但太 了,而且 webpack 配置已经变成了一门在开发者之间像诅咒卷轴一样传递的黑暗艺术。下一个痛点纯粹是关于痛苦本身:工具太难用了。修复方案是用快速的编译型语言------GoRust------来重写它们,而不是用 JavaScript。

esbuild(Go)让打包快了 10--100 倍,让所有人脸上无光。SWC(Rust)在大型框架内部取代了 Babel。而 Vite 把这一切整合成了如今大多数人默认伸手去用的工具:开发时即时启动(依赖浏览器原生模块和 esbuild)、生产环境正式的优化构建、以及 HMR (热模块替换)------你保存文件就能看到变化,不丢失当前页面状态。截至 2026 年,Vite 的生产打包器本身正在被一个 Rust 写的 Rolldown 取代,整个技术栈正在一家公司 VoidZero(已被 Cloudflare 收购)旗下整合。

同样的"为速度重写"浪潮也席卷了包管理器(pnpm 及其巧妙的共享存储、Bun 这个多合一方案)和面向框架群体的打包器(TurbopackRspack)。如果要用一句话概括这一层,那就是每个技术大会都在回响的那句:"我们用 Rust 重写了它,快了 50 倍。"


第五层 --- 28 米 · 2014--2026 年

服务端回归

"我漂亮的应用显示一个白屏,而 Google 看不见它。"

到这时,纯 JavaScript 应用有了一个躲不开的问题:服务器发回一个空的 <div> 和一大堆 JS,用户盯着白屏等它们下载执行完毕,而搜索引擎看着那个空 div,看到的也是一个空白页面。对于内容业务来说,这是致命的。(Google 甚至给了它一个量化指标------Core Web Vitals------于是慢现在会直接损害你的搜索排名。)

修复方案是重新在服务器上渲染 HTML。也就是说:你在 2008 年做的那个事。 缩写词在这里开始疯狂繁殖,一口气列完整个家族:SSR (服务端渲染)按每次请求在服务器上渲染页面。SSG (静态站点生成)在构建时预渲染一次(你的老式静态站,加了工具)。ISR (增量静态再生)是会悄悄自我刷新的 SSG。而编排这一切的框架------元框架(meta-frameworks) ------是如今大多数人实际上直接上手的层:Next.js(React)、Astro(内容优先,几乎不发 JS)、SvelteKitNuxt(Vue)、Remix(现已并入 React Router)。

但把服务端 HTML 发给一个为浏览器构建的框架,制造了现代最标志性的痛点:水合(hydration)

第一步:服务器下厨 --- 它运行你的组件,发送完成的 HTML。页面瞬间绘制出来。它看起来是完成了。

第二步:但它是死的 --- 没有任何按钮能用。HTML 是 UI 的一张照片,不是 UI 本身。没有任何事件处理器被挂载。

第三步:你再付一次费 --- 浏览器下载所有 JS,重新运行同一个应用来"唤醒"它。有那么一刻,用户疯狂点击一个看起来活着但其实没活的按钮。

水合就是为一顿饭付两次钱:一次在服务器上做好,一次在浏览器里再做一遍一模一样的饭来证明它能吃。

这之后所有时髦的东西都是减少水合的方案。岛屿架构(Islands)Astro)保持页面静态,只水合少数几个交互部分。可恢复性(Resumability)Qwik)完全跳过水合。而 React 服务器组件(RSC) ------现代 React 中最令人困惑的概念------让组件只在服务器上运行,向浏览器发送 JavaScript,用一行 "use client" 标记交互的叶子节点。它确实聪明,也确实困难,如果有一个缩写能告诉你你离开后地面发生了移动,那就是这个。


第六层 --- 34 米 · 2015--2026 年

生态成熟:锦上添花

"这个代码库没法维护了,而且我一直在重建同一个下拉菜单。"

应用大到这个程度,两个新痛点浮现了:你无法安全地重构,而且你在永远重建同一个组件。这里的修复方案,坦白说,是最值得采纳的部分。TypeScript 给 JavaScript 加上了类型系统,在你打字的时候就拦截掉整类 bug,还让你的编辑器有了真正的自动补全。这是你离开后最大的单一变化,它赢得了如此彻底的胜利,以至于工具链现在正在把自家的编译器移植到 Go 以跟上速度。

在样式这个恐龙的老家,最大的推动者是 Tailwind:把工具类直接写在标记里(class="px-4 py-2 rounded"),这看起来就像你被教导要避免的内联样式,也确实引发了你能预料到的反应------直到它悄悄成为很多人交付 UI 最高效的方式。与此同时原生 CSS 终于也长大了------嵌套、变量、容器查询:has()------所以很多旧工具根本不再需要了。

而组件不再是你安装的依赖,变成了你拥有 的代码:shadcn/ui(你把源码复制到自己的仓库里,构建在无障碍的 Radix 原语之上)重置了整个品类。把它和 TanStack Query 配合处理服务端数据,用一点 Zustand 处理其余的,用 Zod 做校验,再配上 Vitest + Playwright 做测试------你就看到了 2026 年应用的技术词汇表。

样本 02 --- 按钮,完全挖掘 · 2026 年

jsx 复制代码
// 有类型、已水合、有设计令牌、
// 复制而非安装、服务端绑定。

<Button
  variant="default"
  size="sm"
  onClick={addToCart}
>
  Buy
</Button>

// + 47 个文件:构建、类型、
// CSS 引擎、无障碍和运行时。

默认无障碍。通过设计令牌 做主题。端到端类型检查。公平地说,这是一个更好的按钮。但它也耗费了一整个生态系统才渲染出"Buy"这个词。

  • 源码体积:~90 字节
  • 背后:node_modules 里约 25 万个文件
  • 构建步骤:

第七层 --- 39 米 · 2015--2026 年

部署重生:轻舟已过

"好吧。我怎么把它放到互联网上?"

纯粹的好消息层。还记得把文件拖进 FileZilla 然后祈祷的日子吗?没了。现在你把 Git 仓库连接到 VercelNetlifyCloudflare,每次 git push 就自动构建和部署你的网站。开一个 pull request,你就得到那个具体改动的实时预览 URL。这部分未来毫无疑问比 2008 年好。

两个值得记住的概念。Serverless(无服务器)函数让你运行后端代码而不需要运维服务器。边缘(edge) 意味着代码在靠近用户的数百个数据中心运行,而不是在一个机房里------整个星球都是你的源站。如果你想把 Web 技能带到手机和桌面上:React Native/Expo 做原生应用,TauriElectron 做桌面(Electron 为了给你显示一个聊天窗口而搭载一整个浏览器;Tauri 用你已有的那个)。


第八层 --- 44 米 · 2023--2026 年

机器代笔

"说实话,能不能让机器直接帮我写 React?"

最深、最新的一层,也是把很多人拉回来看一眼的那层。你现在可以用大白话向 v0LovableBolt 描述一个 UI,然后拿回一个能跑的前端;像 CursorClaude CodeCopilot 这样的编辑器在你打字时就写好了样板代码。这个做法甚至有了一个名字------vibe coding(凭感觉编程),由 Andrej Karpathy 创造------附带一个诚实的星号标注:你仍然需要理解产出的东西是什么。

这也是为什么工程分支之间的界限在模糊。一个后端或系统工程师现在可以在一个下午生成一个像样的前端,这很棒,也稍微有点让人措手不及,因为生成的代码悄悄假设你了解上面八层的所有东西。机器人写的和你理解之间的那个差距,正是这张地图存在的意义。


基岩 --- 44 米 · 2026 年

深潜四十四米,兜兜转转又回起点

底部是这些------也是这趟旅程值得的原因。2026 年的前沿,那些最厉害的人最兴奋的东西,是:在服务器上渲染 HTML、几乎不发送 JavaScript、使用 Web 平台而不是对抗它。Astro、岛屿架构、服务器组件、htmx------"钟摆正在摆回来"------所有这些都指向同一个方向:一个主要由 HTML 构成、从 CDN 服务的快速页面。

也就是说,经过二十年的构建工具、打包器和水合方案,一路下潜四十四米之后,行业跑了一个巨大的圆圈,气喘吁吁地到达了一个看起来和你当年通过 FTP 上传的文件非常相似的地方。直觉没有过期------脚下的地基确实移动过,每一步都有其道理,而如今,它又回归了原位。

html 复制代码
<!-- 街角书店 -->

Welcome. <a href="#">Browse the shelves</a> or <button type="button">Buy</button> a book now.

<!-- 以 HTML 提供。约 3 KB。无构建步骤。
     你还没读完这段说明,它就已经加载完了。 -->

你不必把整座大教堂从头学起。真正需要掌握的,只是那 20% 承重墙般的骨架;其余 80%,权当自助餐,各取所需即可。欢迎回来。你不是来迟了,而是恰逢其时。


实用工具包

这不是强制清单,而是供你挑选的起点。下面列出的都是热门且维护良好的默认选择,但绝不是唯一答案。点击任一工具即可访问其官网。

内容站 / 博客 / 营销页 --- 贴近你的直觉

  • Astro --- 几乎不发送 JavaScript
  • Tailwind --- 或就用原生现代 CSS
  • Vercel / Netlify / Cloudflare --- git push 即部署
  • 或者更轻:htmx 配你已有的后端,或在 CDN 上放纯手写 HTML。2026 年仍然完全成立。

真正的应用(登录型产品) --- 成熟路径

底层工具带 --- 选一次,忘掉它

  • Vite --- 或你框架的 CLI
  • pnpmBun --- 比 npm 快
  • Prettier + ESLint --- 或 Biome 一个搞定两件事
  • Vitest + Playwright --- 单元 + 端到端
  • 用 git push 部署。你这辈子可能永远不会打开一个 webpack 配置文件。很好。

缩写 / 概念速查表

缩写 全称 含义
SPA Single Page Application(单页应用) 一个 HTML 外壳;JavaScript 构建和切换一切;无全页刷新。
SSR Server-Side Rendering(服务端渲染) 服务器按请求构建 HTML,然后浏览器水合它。
SSG Static Site Generation(静态站点生成) 部署时预构建所有 HTML 并提供静态文件。你的老式网站,加了工具。
RSC React Server Components(React 服务器组件) 只在服务器上运行的组件,向浏览器发送零 JavaScript。
Hydration 水合 在浏览器中重新运行 JavaScript,使服务端渲染的 HTML 变为可交互。代价昂贵的部分。
Islands 岛屿架构 大部分静态的页面,带有小型交互"岛屿";只为那些部分发送 JS。
HMR Hot Module Replacement(热模块替换) 保存文件,无需全页刷新即可即时看到变化。
ESM / CJS ES Modules / CommonJS 现代的 import 标准与 Node 旧的 require。
JSX --- 写在 JavaScript 内部的类 HTML 语法。需要转译。不是真正的 HTML。
CWV Core Web Vitals(核心网页指标) Google 的速度指标(绘制、响应性、布局抖动),使性能成为排名因素。
The edge 边缘 你的代码运行在靠近用户的数百个 CDN 节点,而非单一数据中心。

作者简介:我更像是个系统工程师。但到了 2026 年,随着编程代理模糊了每个工程分支之间的界限,我抬头一看,发现自己在看别处的时候前端世界已经发生了这么多事。这是我希望有人递给我的一张地图,写给每一个离开了又正在考虑回来的人。


版权与许可

本文为翻译作品,基于原作 The Descent --- What Happened to the Frontend While You Weren't Watching(作者:David Poblador i Garcia)。

原作与本翻译均采用 CC BY-SA 4.0 许可协议。

相关推荐
不好听6131 小时前
前端路由完全指南(上篇):从原理到 6 种路由模式
前端·react.js
像我这样帅的人丶你还1 小时前
🚀大文件上传的那些事
前端·javascript·架构
不好听6131 小时前
前端路由完全指南(中篇):React Router Hooks 完全指南
前端·react.js
hunterandroid1 小时前
[鸿蒙从零到一] ArkUI 声明式 UI 基础实战
前端
七牛开发者1 小时前
RAG 工程里的上下文剪枝:如何减少 68% 的上下文
前端·css·github
橘子星2 小时前
从零理解流式输出 —— 一个 Vue + DeepSeek 的前端实战
前端·javascript·人工智能
智驭未来掌门人2 小时前
如何让你的前端服务在服务器上nohub运行
前端
颜进强2 小时前
Claude Code - 18 从 Figma 到 React:一套让 AI 生成代码"不瞎编"的实战工作流
前端·后端·ai编程