复制成功
请遵守本站许可
10923 字
55 分钟

36年回顾:互联网前端发展史

学习计算机科学就是在学习计算机历史

0. 引#

0.1 Only screen is real#

我们常把互联网比作冰山,在冰山之下,数以万计的交换机/服务器/数据库在轰隆作响

而对于屏幕前的用户,某个拿着手机坐在地铁里的人,或者坐在办公桌前盯着显示器的白领而言,只有屏幕上的像素点是真实的

如果那个”commit”按钮点下去没有反应;如果那个列表滑动起来一卡一卡;如果那个页面在加载时白屏
那么,无论后端的架构多么优雅,算法多么精准,对于用户来说,这就是一个 垃圾软件

在很长一段时间里,前端被戏称为切图仔
在软件开发的鄙视链中,前端曾经往往处于底端
后端工程师认为前端只是写写 HTML 标签、描描样式,不需要懂内存管理,不需要懂并发控制,甚至不需要懂真正的编程语言

然而,这三十年的历史证明了这种偏见的荒谬。前端开发正在经历计算机科学史上最剧烈,最残酷的演变。我们从最初仅仅是为了展示一篇物理论文,演变到现在要在浏览器这个沙盒里运行 Photoshop、3D 游戏、甚至是 IDE

本篇blog要讲的,不是 API 的流水账,而是这三十年来前端的「三国演义」

观前提醒:本文是以我的个人视角叙述,难免有误,还请各位谅解/多多指出错误。

0.2 原罪#

要理解前端这三十年为什么这么闹腾,为什么框架层出不穷,为什么工具链复杂到让人头秃,我们必须回到原点,去审视 Web 诞生的初衷

WWW 在 1990 年诞生时,Tim Berners-Lee 爵士的设计目标非常纯粹:为了让科学家们方便地共享文档

请记住文档这两个字。这是前端所有痛苦和复杂的根源,也是我认为的原罪

  • HTML 是用来描述文档结构的
  • HTTP 是无状态的协议,以此请求文档,拿完就走,不保持连接
  • 浏览器 只是一个文档阅读器

随着商业化的爆发,人类越来越想要更多。我们要的不再是一张静态的纸,我们要的是一个App

  • 我们要在这个文档阅读器里做Chat
  • 我们要在这里做交易所
  • 我们要在这里协同编辑 Excel
  • 我们要在这里看 HDR 视频

这就好比要求你在 Microsoft Word 里开发一个 Minecraft

前端这三十年在做的事情,本质上就是:
利用一系列原本为静态文档设计的简陋技术,通过各种奇技淫巧,强行构建出媲美原生操作系统软件的动态应用

  • 因为 HTTP 无状态,所以我们发明了 Cookie 和 Session,后来又搞出了 JWT
  • 因为 HTML 每次点击都要刷新整页,体验太差,所以我们发明了 AJAX,强行把网页变成了 SPA
  • 因为 DOM 操作太慢,所以我们发明了 Virtual DOM,在内存里算好了再去碰真实的 DOM
  • 因为 JavaScript 诞生时没有模块系统,所以我们发明了 CommonJS, AMD, UMD, 最终逼出了 ES Modules

前端的演进史,就是一部与浏览器的对抗史。我们在对抗浏览器的限制,对抗网络的延迟,对抗设备的碎片化

0.3 轮回#

在读本Blog时,你会发现一个非常有趣的现象:技术的轮回

  • Web 1.0:网页是服务端渲染的。PHP/JSP 在服务器把 HTML 拼好,浏览器直接展示。那是静态的极致
  • Web 2.0:为了交互体验,我们把逻辑全搬到了浏览器。浏览器成了客户端,服务器只吐 JSON 数据。这是动态的极致
  • Now:为了首屏速度和 SEO,我们发现客户端渲染太慢了。于是,Next.js 和 Nuxt 又出现了

人类绕了一圈,似乎回到了原点?

当年的静态,是因为我们做不到动态 现在的静态,是因为我们掌握了动态之后,为了极致体验而做出的主动选择

0.4 谁是前端?#

在 2000 年,只要会写 <table> 布局和 <font> 标签,你就是前端(那时候其实叫网页设计师)
在 2010 年,只要你会用 jQuery 的 $('#id').click(),你就是前端 现在,你需要会Ts,即时编译,HTTP/3,服务端容器化等等

前端的边界在无限扩张。 向上,我们在侵蚀后端的领地推出Serverless(尽管现在不尽人意)
向下,我们在通过 WebAssembly 调用 C++/Rust 的能力
向外,我们在通过 React Native / Flutter 构建原生App

但无论技术栈如何变迁,有一点我希望阅读本文的前端开发者记住:

我们是离用户最近的人

1. 最初的起点#

1.1 蒂姆·伯纳斯-李的拼图#

一切的起点,并不宏大。没有风险投资,没有 IPO,甚至没有想改变世界

1989 年,蒂姆·伯纳斯-李(Tim Berners-Lee)在 CERN 工作时,面临着一个非常具体的痛点:物理学家们的论文和数据太难找了。它们分散在不同的计算机上,格式各异,如果你想引用别人的研究,只能手动去查

于是,他提出了一个构想:World Wide Web

为了实现这个构想,他拿出了三块拼图,这三块拼图至今仍是互联网的基石:

  1. HTML:一种简单的标记语言,用来描述文档的结构(标题、段落、列表)。这里你要注意的是,它只负责结构,不负责好看
  2. HTTP:一种无状态的传输协议。你请求一个文档,服务器给你,然后连接断开。这种阅后即焚的特性决定了 Web 最初只是一个图书馆,而不是「操作系统」
  3. **URL **:给世界上的每一个文档分配一个唯一的地址

1990 年圣诞节,他在那台 NeXT 电脑上写出了世界上第一个浏览器和第一个服务器

那时的网页长什么样? Only text 只有白纸黑字和蓝色的超链接。图片还需要弹出一个新窗口才能看

这时候的前端开发,和写word没什么区别。你只需要掌握 <h1><p><a> 这几个标签就够了。这是一个Read-Only的时代

1.2 Table 与 Spacer GIF#

随着 Mosaic 浏览器的发布(第一个广泛流行的图形化浏览器),图片终于可以和文字混排了。商业公司开始入驻互联网,设计师们带着平面设计的思维来了

炸了

设计师说:我要在这个角落放个 Logo,在大标题下面分三栏排版,右边还要有一个Guide
工程师看着简陋的 HTML 说:把哥们当日本人整呢

当时的 HTML 只有文档流。没有 Float,没有 Position,更没有 Flexbox

怎么办?聪明或者说被逼无奈的开发者们发现了一个漏洞:HTML 里有一个用来展示数据的标签<table>,也就是表格

如果你把边框设为 0 (border="0"),表格线不就看不见了吗?
如果你在表格里套表格,不就可以切分出复杂的版块了吗?

于是,表格布局时代开始了

  • 切图仔诞生:设计师在 Photoshop 里把一张精美的网页设计图切成几十个小豆腐块,然后前端工程师用一个巨大的 <table> 把它们拼回去
  • 嵌套地狱:为了实现一个圆角框,你可能需要一个 3x3 的表格,四个角放圆角图片,中间放内容。如果内容里还有布局,那就再套一个表格。代码看起来像是一个巨大的迷宫
  • Spacer GIF:这是那个时代最伟大的「黑客技术」
    • 表格单元格有时会塌陷,或者你想要精确的 10px 间距,怎么办?
    • 你制作一个 1x1 像素的透明 GIF 图片
    • 然后写上 <img src="spacer.gif" width="10" height="1">
    • 你看不到它,但它撑开了空间

这是前端工程史上第一次大规模的为了目的不择手段。虽然代码语义极差,维护极其困难(改一个字可能导致整个表格崩塌),但它让网页第一次有了设计感

1.3 JavaScript 的急就章#

1995 年,Netscape 正处于第一次浏览器大战的风口浪尖。他们面临一个巨大的压力:让网页动起来

当时的痛点非常低级:表单验证
那是拨号上网的年代,网速慢得令人发指。用户在网页上填完一个长长的注册表单,点击提交,数据传到服务器,服务器发现用户名为空,再把页面传回来告诉用户报错。这一来一回,可能要几十秒甚至一分钟。用户直接炸了

网景觉得,必须有一种脚本语言,能直接在浏览器里检查表单,不需要经过服务器

这个任务交给了 Brendan Eich

  • BOSS的需求:一定要像 Java(Java 当时如日中天,网景想蹭热度
  • Brendan 的私心:他其实想把 Scheme 或者 Self 搬进浏览器。他喜欢函数式编程
  • 结果:为了赶在 Netscape Navigator 2.0 发布前上线,他只用了 10 天

这门语言最初叫 Mocha,后来改名 LiveScript,最后在发布前夕,为了营销,改名为 JavaScript

这就是后来无数人吐槽 JS 的根源:

  • 它名字里有 Java,但跟 Java 没有任何关系
  • 它的语法像 Java,骨子里却是函数式语言
  • 它的设计充满了仓促的妥协:
    • nullundefined 同时存在,甚至 typeof null === 'object' 这种著名的 Bug 被保留至今
    • 自动分号插入带来的莫名其妙的报错
    • 极其宽松的类型转换([] + [] = ""

它被设计为一个玩具语言,仅仅用来做一些鼠标滑过图片变色、弹出 alert 对话框、检查输入框不为空的小把戏。

当时的专业程序员 (比如写Java的) 对 JavaScript 是嗤之以鼻的。他们认为这不是编程,这是脚本片段

但,潘多拉盒子已经打开了

哪怕它再简陋,它赋予了浏览器逻辑计算的能力 有了逻辑,网页就不再只是文档**,它开始向app进化

那时的前端,就在这一堆乱七八糟的表格标签、透明图片占位符、以及几行简陋的脚本验证代码中,跌跌撞撞地开启了 Web 的商业化时代

而就在不远的前方,一个名叫 Microsoft 的巨头马上就要睡醒了


2. 第一次浏览器大战#

2.1 巨头的坠机#

1996 年,Netscape Navigator 占据了近 80% 的市场份额。那时的浏览器是收费软件(或者说是共享软件),它是通往互联网的唯一大门

但比尔·盖茨醒了。他发出了那封著名的《互联网浪潮》备忘录,微软这艘航母开始全速掉头

微软采用了经典的Embrace, Extend, Extinguish战略:

  1. 拥抱:微软买下了 Spyglass Mosaic 的授权,改造出了 **Internet Explorer **
  2. 扩展:为了差异化竞争,双方开始疯狂地往 HTML 里塞私货
    • Netscape 发明了 <layer> 标签和 <blink>(闪烁文本,我认为是史上最烦人的标签)
    • 微软发明了 <iframe ><marquee>(滚动字幕)
    • Netscape 用 document.layers 操作元素,微软用 document.all
    • 结果开发者必须写大量的 if (document.all) { ... } else { ... }。这就是浏览器嗅探代码的鼻祖
  3. 毁灭:微软祭出了核武器:免费捆绑。IE 被预装在 Windows 95/98 里。网景唯一的收入来源断了

1998 年,网景惨败,被 AOL 收购。但在临死前,他们做出了一个改变历史的决定:开源(噔 噔 咚

他们把 Netscape 混乱的代码全部扔给了社区,成立了 Mozilla。这颗种子在地下埋藏了许多年,终长成名为 Firefox 的大树

2.2 IE6:帝国的荣光与坠机#

2001 年 8 月,Windows XP 发布,随之而来的是 Internet Explorer 6.0

我们需要客观地评价这位昔日的王者。在 2026 年回看 IE6,它是落后、各种 Bug、不安全的代名词。但在 2001 年,IE6 是当时世界上最先进的浏览器,没有之一(毕竟不能拿现在的眼光审视过去

  • 它是最好的 CSS 渲染器:相比之前的版本,它对 CSS1 的支持已经算不错了(虽然有著名的盒模型 Bug)
  • 它是最快的 JS 引擎:那时候的 V8 还在娘胎里
  • 它带来了 AJAX 的火种ActiveXObject("Microsoft.XMLHTTP") 正是随 IE5/6 引入的

IE6 迅速占据了 95% 的市场份额。微软赢了,于是他们解散了浏览器团队(笑死 Web 的时间停止了

从 2001 年到 2006 年,整整五年,浏览器技术没有任何实质性的进步。这五年我称为**“Web 的黑暗中世纪”**

对于前端(这时候开始有专职的前端了),这五年是与 Bug 搏斗的五年

  • 盒模型 Bug:标准说 width 是内容宽度,IE6 说 width 是包含边框的宽度。于是我们学会了 CSS Hack:_width: 200px;(只有 IE6 认识下划线开头的属性)
  • PNG 透明问题:IE6 不支持 PNG-24 的半透明,看到的都是灰色背景。我们被迫写滤镜代码 (AlphaImageLoader) 或者继续用 GIF
  • 双倍边距 Bug:浮动元素的 margin 会莫名其妙加倍。解决办法?display: inline

我们不再是构建者,而是修东西的。前端开发的门槛变成了一门玄学,你必须背诵几十个 IE6 的怪癖才能写出一个正常的页面

2.3 DHTML 与 Flash#

在 HTML/CSS 停滞不前、标准分裂的年代,人类对动态交互的渴望并没有消失。这种渴望在两个方向上找到了出口

2.3.1 DHTML#

DHTML (Dynamic HTML) 不是一个标准,而是一个营销词汇
它指的是:利用 JS 修改 CSS 属性,让静态的 HTML 动起来

那是特效的年代

  • 网页上会有跟随鼠标飘动的文字轨迹
  • 导航菜单展开时会有弹跳效果
  • 状态栏会有跑马灯文字

大部分前端并不会写 JS,他们是脚本搬运工。他们去 DynamicDrive.com 这样的网站,复制一段几百行的 JS 代码,粘贴到网页里,然后改改参数。JS 在当时被视为一种装饰品,一种让网页看起来很酷(或者很杀马特)的玩具

2.3.2 Flash#

既然 HTML 这么烂,不同浏览器渲染还不一致,那为什么不跳出浏览器呢?

Macromedia Flash(后被 Adobe 收购)横空出世
它实际上是一个浏览器里的“虚拟机”

  • 一致性:不管你是 IE 还是 Netscape,只要装了 Flash 插件,效果完全一样。这是对兼容性地狱最有力的反击
  • 多媒体:它能播放视频、音频(当时 HTML 做不到)
  • 矢量绘图:它能做无损缩放的动画(当时没 SVG 的事情)
  • 强大的语言ActionScript。当 JS 还在处理表单验证时,ActionScript 2.0/3.0 已经是完全面向对象的语言了,有类、继承、接口

在那个年代,最顶级的前端工程师其实是 Flash Developer。他们做出了开心农场,做出了视频网站(土豆/优酷的早期播放器),做出了极其华丽的企业官网

Flash 是 Web 2.0 真正的前奏。
它证明了网页可以不仅仅是文档,而可以是游戏、电影和复杂的应用
但它是一个封闭的帝国,一个黑盒子。搜索引擎看不懂它,移动设备(后来的 iPhone)跑不动它

就在这看似无解的僵局中,Web 正在酝酿一场真正的革命
而在遥远的CA,Google 正在悄悄重写 Gmail 的代码,准备用 IE6 那个被遗忘的 XMLHTTP 接口,给世界一点小小的震撼


3. 网页开始呼吸#

3.1 Gmail 的震撼#

2004 年愚人节,Google 发布了 Gmail。当时所有人以为这是个笑话,因为它提供了 1GB 的免费存储空间(当时主流邮箱只有 2MB)

但真正让极客们震惊的不是容量,而是体验

在此之前,网页交互的模式是*同步的:

  1. 用户点击下一页
  2. 浏览器白屏
  3. 服务器生成新的 HTML
  4. 浏览器重新渲染整个页面

而在 Gmail 里,当你点击邮件标题时,网页没有刷新。列表保留在左边,邮件内容瞬间出现在右边。
这种体验太像原生app了,以至于很多人怀疑 Google 用了 Flash 或者 Java Applet

并没有 他们用的是纯正的、原生的、被微软遗忘在角落里的技术 XMLHTTP

  • 这项技术是微软在 1999 年为了 Outlook Web Access 开发的,随 IE5 发布。但微软自己没重视它,把它当成一个不起眼的 ActiveX 控件
  • Jesse James Garrett 的命名:2005 年,这位 UX 设计师写了一篇著名的文章,将这种“无需刷新页面就能与服务器通信”的技术统称为 AJAX (Asynchronous JavaScript and XML)

从此,网页有了后台线程 它可以在你不注意的时候,悄悄去服务器拿数据。网页开始呼吸了

3.2 JSON 一种偶然的标准#

AJAX 中的 “X” 代表 XML。在当时,数据交换的标准格式确实是 XML 但是,在 JavaScript 里解析 XML 简直是灾难。你得写一堆 getElementsByTagName,代码冗长且慢

这时候,一位名叫 Douglas Crockford 的雅虎架构师站了出来 他发现 JavaScript 有一种原生的数据表示法——对象字面量

// XML
<user><name>Chongxi</name><age>23</age></user>
// JSON
{ "name": "Chongxi", "age": 23 }

Crockford 甚至没有发明它,他只是发现了它。他把这种格式命名为 JSON

  • 它天生就是 JS 对象,解析极其简单(早期直接用 eval(),虽然不安全)
  • 它比 XML 轻量得多(少了那么多尖括号,节省带宽)

这是 Web 历史上最四两拨千斤的胜利。 由于前端开发者的极力推崇,后端工程师发现生成 JSON 也比生成 XML 容易。于是,AJAX 里的 “X” 名存实亡,JSON 统治了世界

3.3 Firebug#

在 2006 年之前,调试 JS 的唯一工具是: alert('Here 1'); alert('Variable x is: ' + x);

开发者像瞎子一样摸索。如果代码出错了,IE 只会左下角报一个黄色感叹号,告诉你缺少对象,行号永远是不准的

2006 年 1 月,Joe Hewitt 发布了 Firefox 的插件 Firebug

  • 它能审查元素:鼠标指哪,高亮哪。上帝啊,我们可以直接在浏览器里改 CSS 看效果了
  • 它有控制台 (Console)console.log() 终于取代了 alert()
  • 它能断点调试
  • 它能抓包

Firebug 是前端工程化的第一块基石。 它让前端开发从玄学瞎猜变成了科学观测


4. Write Less, Do More#

4.1 救世主#

虽然有了 AJAX,有了 Firebug,但前端开发依然极其痛苦 痛点在于:碎片化

  • 要获取一个元素:
    • 标准浏览器:document.getElementById
    • 如果要按 Class 获取:IE6 不支持 getElementsByClassName。你得自己写循环去遍历 DOM
  • 要绑定事件:
    • 标准:addEventListener
    • IE:attachEvent
  • 要获取计算后的样式:
    • 标准:getComputedStyle
    • IE:currentStyle

每个前端工程师的电脑里都有一个 utils.js,里面存着几百行用来处理兼容性的脏代码

2006 年 8 月,John Resig 在 BarCamp NYC 上发布了 jQuery 它的核心理念极其简单粗暴:把所有浏览器差异藏在 $ 符号后面

4.2 链式调用的艺术#

jQuery 发明了一种极具表现力的 API 风格——链式调用 (Chaining)

以前的代码:

var div = document.getElementById('CEPATO');
div.style.color = 'red';
div.style.display = 'block';
div.onclick = function() { alert('clicked'); };

jQuery 的代码:

$('#CEPATO').css('color', 'red').show().click(function() {
alert('clicked');
});
  • Sizzle 引擎:这是 jQuery 的核心。它让 JS 可以像 CSS 一样选择元素(div > ul.list li:first-child)。这在当时是黑科技
  • 隐式迭代$('.item').hide() 会自动隐藏页面上所有的 .item,不需要写 for 循环

jQuery 迅速成为了事实标准。甚至微软后来都在 Visual Studio 里内置了 jQuery 的智能提示 那时候的前端面试题:jQuery 源码读过吗?Sizzle 引擎是怎么实现的?

4.3 copycat 黄金时代#

jQuery 的另一个伟大之处在于它的插件机制 ($.fn.extend) 这开启了前端最早的组件化雏形

  • Lightbox:点击图片,背景变暗,图片放大
  • Carousel:轮播图
  • Datepicker:日期选择器

无数不懂 JS 原理的切图仔,靠着下载 jQuery 插件 + 改 CSS,就能拼凑出一个交互丰富的网站
jQuery 极大地降低了前端门槛,但也埋下了祸根: 大量的面条代码和滥用的 DOM 操作,导致页面性能越来越差


5. 引擎的军备竞赛#

5.1 Speed#

随着 AJAX 和 jQuery 的普及,网页里的 JS 代码量指数级增长
从几百行,变成了几千行,甚至几万行…然后

浏览器跑不动了

当时的 JS 引擎都是解释执行的:读一行,跑一行。效率极低 Web 应用想取代桌面软件,最大的瓶颈就是

5.2 Google 的阳谋#

2008 年 9 月,Google 发布了一本漫画书,介绍了一款新浏览器:Chrome 与之同来的,是一个全新的 JS 引擎:V8

V8 做了一件惊天动地的事:JIT 它不解释代码,它是把 JavaScript 直接编译成机器码运行

  • V8 刚发布时,JS 执行速度比 IE 快了不知道多少倍
  • 多进程:一个标签页崩了,不会导致整个浏览器崩溃。这也是 Chrome 的首创

V8 的出现改变了一切

  • 它证明了 JS 可以跑得飞快
  • 它让 JS 有能力处理极其复杂的逻辑(不仅仅是表单验证)
  • 既然 V8 这么快,而且它是独立的 C++ 库,那为什么不能把它拿出来,在服务器上跑 JS 呢?(而一年后,Node.js 诞生了)

5.3 WebKit#

与此同时,Apple 开源的 WebKit 内核(源自 KHTML)开始崛起。Safari 和早期的 Chrome 都使用了 WebKit 它是移动互联网的基石。第一代 iPhone 发布时,乔布斯不支持 Flash,只支持 Web 标准 这意味着,即将到来的移动 Web 浪潮,将是 WebKit 的天下…吗?

至此,前端已经拥有了:

  1. 交互能力
  2. 数据标准
  3. 开发工具
  4. 标准库
  5. 高性能引擎

万事俱备
但随着代码量的膨胀,我们即将迎来一场巨大的危机
我们有了枪炮,但还缺乏战术。我们写得出炫酷的特效,但维护不了庞大的系统

接下来咱就要讲工程化与架构革命 Node.js 登场,前端终于要开始像正经的软件工程师那样思考了

6. Nodejs#

如果说前二十年,前端还是在浏览器这个沙盒里带着镣铐跳舞,那么从 2010 年开始,前端工程师通过一次惊天动地泣鬼神的越狱,彻底改变了游戏规则

6.1 Ryan Dahl 的疯狂实验#

2009 年的 JSConf EU 大会上,一个名叫 Ryan Dahl 的年轻人做了一场演示

他做了一件看似违背祖宗的事情:他把 Google Chrome 里的 V8 引擎抠了出来,给它装上了文件系统和网络模块,让它跑在了服务器上

这就好比把法拉利的引擎拆下来,装在了一台拖拉机上

这个项目叫 Node.js

起初,大家以为它是后端技术的革命(非阻塞 I/O,高并发)
但很快,前端工程师们发现了一个惊天秘密:补兑,既然服务器能跑 JS,那是不是意味着 我们可以用 JS 来写…

在此之前,如果你想压缩一个 JS 文件,你得用 Java 写的 YUI Compressor
如果你想预处理 CSS,你得用 Ruby 写的 Sass
前端开发者的工具链掌握在别人手里

Node.js 的出现,让前端拥有了造轮子的权利

6.2 npm#

2010 年,Isaac Z. Schlueter 发布了 npm

在这之前,如果我们想用 jQuery,得去官网下载 jquery.js,放到项目文件夹里,然后在 HTML 里写 <script src="jquery.js">。如果 jQuery 依赖了别的库,你根本不知道,直到报错

npm 改变了一切: npm install jquery

这不仅是一个下载工具,它引入了语义化版本控制依赖树 前端代码不再是散落在各处的碎片,而是变成了一个有组织、可复用的模块体系

6.3 自动化构建:Grunt 与 Gulp#

既然有了 Node.js (运行时) 和 npm (包管理),前端工程师开始编写自动化脚本

  • Grunt:基于配置的构建工具。你需要写一个巨大的 JSON 对象,告诉它先把 A 文件和 B 文件合并,然后压缩,然后重命名
  • Gulp:基于流的构建工具。代码写起来像管道一样流畅:
    src('src/*.js')
    .pipe(babel())
    .pipe(uglify())
    .pipe(dest('dist/'));

这是前端工程化的开端 我们不再手动刷新浏览器,不再手动压缩图片 前端开发从手工进化到了流水线


7. MVC 混战与移动大迁徙#

7.1 乔布斯的判决书与 HTML5 的上位#

2010 年 4 月,Steve Jobs 发表了著名的公开信**《Thoughts on Flash》** 他列举了 Flash 的六大罪状(耗电、触摸屏支持差、封闭等),并宣布 iPhone/iPad 将永远不支持 Flash

这一刻,Web 的命运被改写了
移动互联网的大门轰然打开,而唯一的通行证是 HTML5

于是,所有的企业都需要开发移动版网页
但手机的 CPU 和网络比电脑差得多,屏幕也小得多
简单的 jQuery 特效在手机上卡顿无比。我们需要更高效、更像原生 App 的网页

7.2 意大利面条代码危机#

随着需求越来越复杂(比如做一个网页版的 Excel 或 Trello),前端代码量突破了 10 万行
jQuery 的弊端彻底暴露:它把状态藏在了 DOM 里

想象一下,你要做一个购物车

  • 用 jQuery:你需要从 span.total-price 里读出文本,转换成数字,加 1,再写回 span
  • 如果页面上还有三个地方要显示总价呢?你得手动更新三个地方
  • 如果漏了一个怎么办?Bug 就来了

这种面多加水,水多加面的代码,错综复杂,被称为意大利面条代码
一旦项目变大,维护它简直是地狱

7.3 寻找秩序#

为了解决这个问题,前端开始向后端偷师,引入了经典的软件架构模式:MVC
核心思想是:数据(Model)是源头,视图(View)只是数据的投影

7.3.1 第一代探索者:Backbone.js#

Backbone 非常轻量。它给了你 Model、Collection、View 三个基类

  • 它告诉你:把数据存在 Model 里,不要存在 DOM 里
  • 当 Model 变了,Model 会触发一个 change 事件
  • View 监听到事件,手动去更新 DOM(还是用 jQuery)
  • 它建立了秩序,但写起来还是很累,很多胶水

7.3.2 AngularJS (v1#

2009 年发布,2012 年爆火。AngularJS 带来了两个震撼世界的概念:

  1. 双向数据绑定

    <input ng-model="username">
    <h1>Hello, {{ username }}</h1>

    你在输入框里打字,下面的 <h1> 里的文字实时跟着变 不需要写任何 JS 事件监听代码 这在当时简直是魔法

  2. 依赖注入: 你想要用 $http 服务?只需要在函数参数里写上它,Angular 就自动给你送进来

AngularJS 让 HTML 变成了一种模版语言。它让开发效率提升了 10 倍
一时间,全世界都在用 Angular 重写项目。前端工程师觉得自己终于是在开发软件,而不是在写脚本

7.4 AMD vs CMD#

在 Node.js 和框架爆发的同时,还有一个基础问题没解决:JavaScript 语言本身没有模块系统(直到 ES6)

我怎么在一个 JS 文件里引用另一个 JS 文件? <script> 标签不仅丑,而且依赖顺序很难管理(jquery.js 必须在 plugin.js 之前)

于是,民间标准诞生了:

  • CommonJS:Node.js 用的标准 (require/module.exports)。是同步加载的,适合服务器,不适合浏览器(网络请求要等)
  • AMD (RequireJS):异步模块定义。define(['dep1'], function(dep1) { ... })。它是浏览器端的霸主
  • CMD (SeaJS):淘宝玉伯提出的标准,更符合 CommonJS 的书写习惯(就近依赖)

这不仅是技术之争,也是社区话语权之争

至此,我们完成了工业化的初步积累

  1. 我们有了构建工具,开始像正规军一样开发
  2. 我们有了MVC 框架,开始分离数据和视图
  3. 我们有了移动端市场,地位空前提升

但是,AngularJS 的双向绑定在大规模应用中出现了严重的性能问题
而 FaceBook 的工程师们,正在为一个叫做 Instagram 的应用头疼。他们觉得 MVC 模式在前端也是错的
他们提出了一个更激进的想法:如果视图只是一个函数呢?

8. React 与 范式转移#

8.1 JSX 的离经叛道#

2013 年,当 Facebook 的工程师 Jordan Walke 开源 React 时,整个社区的反应是愤怒的
甚至有人说:这是把我们过去十年的努力全毁了

Why? 因为 JSX 在过去,最佳实践是关注点分离:HTML 归 HTML(模版),JS 归 JS(逻辑)
React 却说:不,我们要把 HTML 写在 JS 里

React 的核心洞察是:UI 渲染逻辑(事件处理、状态变化)和 UI 展示逻辑(DOM 结构)本质上是紧密耦合的。硬把它们分在不同文件里,只是物理分离,而不是逻辑分离

JSX 不是模版,它是 JavaScript 的语法糖。这意味着你拥有一门图灵完备语言的所有能力(变量、if/else、循环、函数)来描述界面,而不是只能用蹩脚的模版语法(ng-repeat, v-if

这是一场思维革命:我们不再编写页面,我们在编写组件

8.2 Virtual DOM#

React 被骂得最惨的时候,是它的性能拯救了它。 当时 Facebook 遇到了一个难题:在极其复杂的 Feed 流里,如何高效更新消息?
如果是 jQuery,你要手动找哪个 DOM 变了;如果是 Angular 1,它会疯狂地进行脏检查(Dirty Checking),导致页面卡顿

React 提出了 Virtual DOM

  1. 快照:每次状态变了,我都在内存里生成一棵新的虚拟 DOM 树(纯 JS 对象)
  2. 对比:我用高效的算法(O(n))对比新旧两棵树,找出哪怕一个属性的变化
  3. 修补:我只把变化的这一点点应用到真实的浏览器 DOM 上

这是数学的胜利
React 甚至不需要知道浏览器的存在。它把渲染抽象成了一个函数:UI = f(State) 只要状态变了,UI 就自动变。开发者再也不用碰 document.getElementById 了。手动操作 DOM 成为历史

8.3 Redux 的苦行僧#

React 只解决了 View,那数据怎么办?
Facebook 提出了 Flux 架构,后来Dan Abramov 把它简化为 Redux

Redux 是极其繁琐的:Action, Reducer, Store, Dispatch。改一个字段要写四个文件
为什么我们要这么折磨自己?

因为在大型应用里,可预测性比便捷性更重要

  • 单向数据流:数据只能从上往下流
  • 纯函数:Reducer 必须是纯函数,同样的输入永远得到同样的输出
  • 倒带:因为状态是不可变的,你可以随时回退到之前的状态

Redux 治好了 MVC 时代的状态管理混乱症,虽然代价是大量的样板代码


9. Vue 的渐进式哲学#

9.1 尤雨溪的减法#

就在 React 试图用函数式编程教育世界的时候,一个 Google 的前员工尤雨溪在想:“有没有一种办法,既能有 Angular 的模版便利性,又有 React 的虚拟 DOM 性能?”

2014-2015 年,Vue.js 开始爆发
它的核心哲学是渐进式

  • 你想用 CDN 引入写个小挂件?没问题,像 jQuery 一样用
  • 你想写个单页应用?没问题,上 Vue Router 和 Vuex
  • 你想搞大型工程?没问题,上 Vue CLI

Vue 没有强迫你学会 JSX,它保留了 HTML 模版。这让无数从 HTML/CSS/jQuery 时代过来的开发者感到了温暖。在大中华区,Vue 成为了绝对的统治

叠甲:用「大中华区」表述单纯是因为个人习惯,不喜勿喷

9.2 响应式魔法 Object.defineProperty#

Vue 2.0 最迷人的地方在于它的响应式系统 它利用了 ES5 的 Object.defineProperty(Getters/Setters)

  • 当你把一个对象传给 Vue 的 data 时,Vue 会遍历它的所有属性,把它们变成 Getter/Setter
  • 当组件渲染时,读了哪个属性,Vue 就记下来这个组件依赖这个属性
  • 当属性变了(Setter 被调用),Vue 通知所有依赖它的组件更新

这比 Angular 1 的脏检查高效,比 React 的手动 setState 自动化。它是魔法,但对于大多数开发者来说,这是一种幸福的魔法

10. 构建的巴别塔#

10.1 Webpack 炼金术#

在这个时期,前端工程师最痛恨的文件大概是 webpack.config.js 随着 SPA的普及,一个项目可能有几百个 JS 文件、几十个 CSS 文件、几百张图片

Webpack 站出来说:在我眼里,Everything is a module

  • JS 是模块,CSS 也是模块(import './style.css'),图片也是模块(import img from './logo.png'
  • Loader:它是一个转换器,把 Sass 转成 CSS,把 ES6 转成 ES5,把图片转成 Base64
  • Plugin:它是一个大管家,负责压缩代码、分割代码(Code Splitting)、注入环境变量

Webpack 极其强大,也极其复杂。它甚至催生了一个新工种 Webpack 配置工程师 但也正是 Webpack,让前端终于有了工业级的构建流水线。我们终于可以做 Tree Shaking(摇树优化,用于去掉没用的代码),做按需加载

10.2 让我们活在未来#

2015 年 6 月,ES6 (ECMAScript 2015) 正式发布
这是 JavaScript 诞生以来最大的更新:class, module, arrow function, promise, const/let
JS 终于像一门正经语言了

但是,浏览器(尤其是万恶的 IE)不支持啊
Babel(原名 6to5)出现了

它是一个编译器。它把 ES6 代码读进去,转换成等价的 ES5 代码吐出来
这意味着:前端开发者不再受限于用户的浏览器版本
我们可以使用 2026 年的语法标准写代码,通过 Babel 跑在 2010 年的浏览器上

这是前端自信心的极大提升:我们掌控了语言的发展权

10.3 TypeScript 的浪子回头#

到了 2018 年左右,随着项目规模突破百万行代码,弱类型的 JS 成了最大的隐患
“Cannot read property ‘x’ of undefined” 是所有线上事故的罪魁祸首

Microsoft 的 TypeScript 终于熬出头了

  • 它是 JS 的超集
  • 它带来了静态类型检查
  • 它带来了极其强大的 IDE 智能提示

起初,大家嫌弃写类型麻烦(把Ts写成anyScript 但当你在重构一个 5 年前的老组件,IDE 精准地告诉你哪里报错时,你会跪下来感谢 TypeScript
无 Ts,不前端逐渐成为了行业共识

至此,现代前端的四大支柱确立了:

  1. 组件化框架
  2. 类型系统
  3. 构建工具
  4. 现代语法

我们建立了一座宏伟的巴别塔 但是,这座塔越来越重了 node_modules 文件夹变成了黑洞(没那么轻)。一次 npm install 要 5 分钟,一次 npm run build 要 3 分钟(没那么快) SPA 的首屏加载越来越慢,SEO 依然是痛点

历史的钟摆,即将再次摆动
为了追求极致的速度,我们将开始反思:我们是不是把太多东西塞进浏览器了?

11. 静态复兴#

11.1 SPA 的代价#

在 2016-2019 年,如果你不做 SPA,你都不好意思说自己是前端
但是,当全世界都用 React 写网页时,问题暴露了:

  1. TTFP:用户打开网页,必须先下载几兆的 JS bundle,浏览器解析完,才能渲染出第一行字。在 3G 网络下,这意味着 5-10 秒的白屏
  2. SEO 归零:Google 的爬虫虽然能执行 JS,但Bing或者百度的爬虫经常抓瞎。对于新闻、电商网站来说,搜不到等于死

CSR的神话破灭了

11.2 Next.js 与 Nuxt#

Vercel(Next.js 背后的公司)站了出来
他们的思路很简单:既然浏览器渲染慢,那我们回服务器渲染不就行了?

  • 同构渲染
    • 用户第一次访问:服务器跑 React 代码,生成 HTML 字符串,直接返回。速度极快,爬虫友好
    • 用户后续点击:浏览器接管,变成 SPA。体验丝滑

这听起来很像 1998 年的 PHP/JSP,但区别在于:前后端是一套代码(全是 JS/TS)。前端工程师拿回了渲染的主导权,同时兼顾了性能

11.3 静态生成#

对于博客、文档、营销页,内容几百年不换一次,为什么要每次请求都算一遍?
Gatsby 和 Next.js 推广了 SSG

  • 构建时:在开发者电脑上,React 把 1000 篇博客编译成 1000 个 .html
  • 分发:扔到 CDN 上
  • 访问:用户访问时,就是从离他最近的 CDN 节点拿一个静态文件。这是物理定律允许的最快速度

我们绕了一圈,回到了 Web 1.0 的静态文件模式,但生产这些文件的,是 Web 4.0 的自动化流水线


12. 水合与欺骗的艺术#

12.1 恐怖谷效应#

SSR 和 SSG 带来了一个新问题:“注水
当服务器返回 HTML 时,网页看起来画好了,按钮也在那儿
用户急不可耐地去点按钮:没反应

为什么?因为 JS 还没下载完,事件监听器还没挂载上去
这段时间被称为恐怖谷。用户以为它是活的,其实它是死的

这是现代前端最大的痛点:为了让用户早点看到内容,我们不仅发了 HTML,还得再发一份一模一样的 JS 去激活它。数据发了两遍

12.2 岛屿架构#

2021 年,Astro 提出了一个灵魂拷问: 我的blog文章是静态的,为什么我要加载 React 库来渲染它?

Astro 的方案是 0 JS

  • 整个页面是纯 HTML
  • 只有那个评论区组件和点赞按钮是 React 组件
  • 浏览器只加载那两个小岛的 JS

这是对 React 全家桶的反叛。它标志着前端开始追求细粒度的加载。我们不再把一头大象塞进冰箱,我们只放进去几根香蕉

这也是我个人是非常喜欢Astro的原因之一

12.3 边缘计算#

现在的后端,不再仅仅只是机房里的一台服务器,而是遍布全球的 Edge Functions
Vercel / Cloudflare Workers 允许前端工程师写一段 JS 代码,部署到全球 200 个城市

用户的请求会被路由到离他最近的节点,那里有一个微型的 V8 环境在等他
前端工程师的边界,已经拓展到了网络的最边缘

13. 工具链的降维打击#

13.1 Vite 闪击前端#

2020 年,尤雨溪(又是他)发布了 Vite 当时 Webpack 的痛点是:启动一个大项目开发服务器,需要等 30 秒甚至 1 分钟。因为 Webpack 要把所有文件打包一遍

Vite:现在的浏览器已经支持 import 了,为什么还要打包?

  • No-Bundle 开发:Vite 启动时几乎不做事。你请求哪个文件,它就编译哪个文件。启动时间:300ms
  • 这种体验上的巨大差异,让 Vite 像病毒一样迅速吞噬了 Webpack 的市场份额

13.2 Rust 与 Go 的入侵#

Vite 的底层使用了 esbuild(用 Go 写的) Next.js 的底层使用了 SWC / Turbopack(用 Rust 写的)

前端工具链正在经历一场**“降维打击”**。

  • 以前:用 JS 写 JS 的编译器(Babel, Terser)。慢,单线程
  • 现在:用系统级语言(Rust/Go)写 JS 的编译器。快,多核并行
  • 结果构建速度提升了 10 倍到 100 倍

这标志着前端基建的门槛被极大地拔高了。以前你会写 JS 就能造轮子,现在你得懂 Rust 和内存管理

13.3 CSS 的原子化:Tailwind CSS#

在样式领域,一场审美审丑的战争结束了 Tailwind CSS 赢了

它看起来很像 1999 年的内联样式:<div class="flex items-center justify-between p-4 bg-blue-500"> 很多人第一次看觉得丑爆了 但用久了就感觉爆赞了:

  • 不用想类名:再也不用纠结 container-inner-wrapper 这种名字了
  • 没有死代码:CSS 文件不再无限膨胀
  • 设计系统:颜色、间距都是标准化的

它改变了前端写样式的肌肉记忆

14. 前端已死,前端万岁#

14.1 全栈的回归#

React 最近推出了 RSC 你可以在 React 组件里直接写 SQL 查询数据库:

// This runs on Server
async function UserList() {
const users = await db.query('SELECT * FROM users');
return <div>{users.map(u => <p>{u.name}</p>)}</div>;
}

前后端的界限彻底模糊了(但这也导致了更逆天的问题出现
前端工程师不再是切图仔,也不再是API 消费者
我们正在变成产品工程师。我们一个人就能构建一个完整的、高性能的应用

14.2 AI#

2023 年,ChatGPT 等各家LLM来了 写一个居中布局?写一个正则验证?写一个复杂的 Reducer?
AI 一秒钟就生成

前端开发的门槛到底是变低了还是变高了?

  • 低级代码 的门槛消失了。AI 会比你写得快,被AI取代的也是这些
  • 系统架构 的门槛变高了。你需要懂 SSR、Hydration、Database、Security等乱七八糟的东西,才能指挥 AI 把它拼成一个好的产品

14.3 下一个三十年#

回顾这三十年: 从 1990 年的 <table> 布局,到 2025 年的 Rust 编译器驱动的 Edge SSR

  • 我们曾为了,把一切搬到浏览器
  • 我们现在为了,把一切搬回服务器

历史不是简单的圆圈,而是螺旋上升的塔
我们回到了服务器,但我们带回了组件化、声明式编程和极致的工程化体系

Web 依然年轻 只要人类还需要通过屏幕与数字世界交互,前端工程师的使命就永远不会结束

15. 结#

至此,我们过了一遍属于前端的三国演义
我们从从蒂姆·伯纳斯-李的草稿纸开始,一直讲到现在 这就是无数开发者与浏览器搏斗、妥协、并最终驯服它的过程

为什么要学这么多乱七八糟的工具?
因为我们是在一堆原本用来擦屁股的纸上,通过几十年的叠Buff,硬生生盖出了一座摩天大楼

36年回顾:互联网前端发展史
https://blog.chongxi.us/posts/2026/02/08/historyForFR/
作者
Chongxi
发布于
2026-02-08
许可协议
CC BY-NC-SA 4.0
End of Content