从一张文档到一片大陆:Web的诞生与浏览器战争
第 1 章 从一张文档到一片大陆:Web 的诞生与浏览器战争
1989 年 3 月 12 日,CERN(欧洲核子研究组织)的一间办公室里,Tim Berners-Lee 把自己写了一个月的提案文件打印了出来。这份名为《Information Management: A Proposal》的备忘录只有寥寥几页,字里行间透着工程师式的克制:它没有使用"革命"或"未来"这样的字眼,只是冷静地描述了粒子物理学家面临的现实困境——人员流动频繁、项目之间关系复杂、文档散落在数百台不同的计算机上,没有人能把它们串起来。三十五年后,这个提案孕育的系统承载着全球 50 亿人的日常信息生活。然而,本章要追问的第一个、也是最容易被忽略的问题是:Web 为何选择了 URL、HTTP、HTML 这套三角架构,而不是一个集大成的"超级协议"?这个看似平淡的设计选择,实际上包含了分布式系统设计中最重要的三个原则——标识、传输、呈现的彻底解耦。
1.1 Web 三角架构的工程哲学:标识、传输与呈现的三重分离
理解 Web 的起点,不是某一行代码,而是 Berners-Lee 在 1980 年写的另一套系统——Enquire。那是一个用 NeXT 计算机实现的"个人信息备忘录"程序,本质上是一个超文本网络:每个节点是一张卡片,卡片之间通过双向链接相互引用。可惜 Enquire 没有网络通信能力,它只能运行在单机上。1984 年 Berners-Lee 离开 CERN 去做别的工作,1989 年回到 CERN 后,他发现整个欧洲的高能物理实验室都面临同样的信息碎片化问题,Enquire 的思路可以被放大成一个全球性的系统。
要把 Enquire 的思路变成现实,Berners-Lee 必须解决三个相互独立的工程问题。
第一个问题:如何唯一标识一个资源? 在 1989 年的网络世界里,FTP 服务器要求用户记忆 `ftp.cern.ch`、路径、文件名、目录;Gopher 服务器用层级菜单导航;Usenet 用新闻组名称。Berners-Lee 在 1990 年给出的答案是 URI(Uniform Resource Identifier),后来被细分为 URL(Locator)和 URN(Name)。URI 的设计哲学极其激进:它必须是全局唯一的、可分层的、可移植的。一份写于 1994 年的草案最终演化成 RFC 2396,又在 2005 年成为 RFC 3986。一个 URI 看起来像这样:
``` https://example.com:443/path/to/resource?query=value#fragment ```
它由五部分组成:方案(scheme)、授权(authority)、路径(path)、查询(query)、片段(fragment)。这种"分段式"设计绝非偶然——它把"如何找到资源"、"如何命名资源"、"如何定位资源内部某一部分"这三个问题拆开,让每一段都可以独立演化。比如 `http://` 和 `https://` 只是方案不同,但路径语法完全相同;`?query=value` 后的部分可以由服务器端自由处理,而 `#fragment` 完全由客户端处理。可演化性(evolvability) 是 URI 设计最被低估的遗产——今天的 WebSocket(`ws://`)、Git(`git://`)、BitTorrent(`magnet://`)都借助 URI 的扩展能力融入了 Web 生态,而 URI 本身几乎没动过。
第二个问题:如何在不同计算机之间传输资源? Berners-Lee 给出的答案是 HTTP(HyperText Transfer Protocol)。HTTP 的第一个版本(0.9,1991 年)极度简陋:客户端发起一行 ASCII 文本 `GET /path`,服务器返回 HTML 流然后关闭连接。没有头部、没有状态码、没有方法动词。但这种简陋恰恰是工程智慧的体现。HTTP 1.0(RFC 1945,1996)和 HTTP 1.1(RFC 2616,1999)逐步加入了头部、状态码、方法(GET、POST、PUT、DELETE)、持久连接、Chunked Transfer Encoding,到 HTTP/2(RFC 7540,2015)引入了二进制分帧、多路复用、头部压缩,HTTP/3(RFC 9114,2022)又基于 QUIC 协议重新设计了传输层。
值得注意的是,HTTP 从诞生之日起就坚持无状态(stateless)原则——服务器不记忆任何客户端历史,每次请求都是独立的。这一选择当时引发了大量争议,因为它直接放弃了事务性、放弃了会话、放弃了状态机。然而,正是这种"刻薄"的无状态设计,让 HTTP 具备了无与伦比的可伸缩性:一个请求可以被任意一台服务器处理,可以被任意数量的代理、缓存、CDN 节点转发,可以在不通知服务器的情况下被丢弃或重试。今天支撑千亿级请求的 Web 基础设施,本质上是建立在 HTTP 1991 年的那个简陋选择之上的。
第三个问题:如何表达资源的内容? Berners-Lee 选择直接借用一种已经存在的标准:SGML(Standard Generalized Markup Language)。SGML 是 1986 年发布的 ISO 标准,本质上是一种"元语言"——它不规定任何具体的标签,只规定标签的语法、属性、嵌套规则。SGML 非常强大,但也极其复杂:完整的 DTD(Document Type Definition)文件动辄上百页,解析器实现难度高,专业人士都望而生畏。Berners-Lee 的天才决策是对 SGML 做极端的简化:砍掉 DTD、砍掉属性值类型、砍掉命名空间、砍掉实体引用、砍掉处理指令,只留下最朴素的"成对标签 + 文本 + 嵌套"。这就是 HTML(HyperText Markup Language)。1993 年的 HTML 1.0 规范只有 18 个标签,其中大部分是排版相关(`<h1>` 到 `<h6>`、`<p>`、`<ul>`、`<li>`),少部分是结构相关(`<html>`、`<head>`、`<body>`),再加一个革命性的 `<a>`。
三角架构之所以伟大,是因为它把三个本可以耦合在一起的设计决策彻底解耦。URL 关心"在哪里",HTTP 关心"怎么取",HTML 关心"是什么"。这种分离带来三个深远影响:第一,每个协议可以独立升级——HTTP 从 0.9 升级到 3,HTML 从 1.0 升级到 5 Living Standard,URL 从 RFC 1738 升级到 RFC 3986,三条演进线互不干扰;第二,每个协议可以被替换——CDN 替换 HTTP 的传输实现,模板引擎替换 HTML 的内容生成,反向代理替换 URL 的解析方式;第三,每个协议都可以独立学习——一个前端工程师不需要懂 HTTP 2 的 HPACK 压缩算法,就能写好 CSS;而一个后端工程师不需要懂 CSS Grid,就能正确设置响应头。这种关注点分离不是空话,它是软件工程 50 年来最重要的设计模式,而 Web 是这一原则的最完美体现。
1.2 浏览器战争与标准博弈:混乱如何催生秩序
1993 年 4 月,Mosaic 浏览器发布。它是第一个在 Windows、Macintosh、Unix 上都能运行的图形化浏览器,而且支持在文本中嵌入图片。1994 年 4 月,Mosaic 团队的核心成员 Marc Andreessen 离职创办了 Netscape Communications,同年 12 月 Netscape Navigator 1.0 发布。从这一刻起,Web 进入了长达 30 年、被分成三个阶段的"浏览器战争"。
1.2.1 第一次浏览器战争(1995—2001):从创新到垄断再到崩溃
Netscape Navigator 1.0 发布后迅速占领市场,1995 年市场份额一度超过 80%。但JavaScript 的诞生是这一阶段最具决定性的事件。1995 年 5 月,Brendan Eich 受命在 Netscape 中加入一种"脚本语言",原计划是"让 Scheme 的内核穿上 Java 的外衣",结果 10 天内他写出了初版 LiveScript(后来改名 JavaScript 完全是为了蹭 Java 的热度)。JavaScript 的设计充满缺陷:变量提升、自动分号、混乱的相等运算符、`with` 语句、`==` 的隐式转换——但它的原型继承(prototype-based inheritance) 和头等函数(first-class functions) 构成了后来函数式编程浪潮的基础。JavaScript 1.0 随 Netscape Navigator 2.0 在 1995 年 12 月发布,IE 3.0 在 1996 年 8 月加入了 JScript(与 JavaScript 不完全兼容),Web 从此进入"两个解释器"时代。
浏览器厂商在 1996 年发起了一场标准争夺战。W3C 在 1996 年 12 月发布了 CSS 1 规范,作者是 Håkon Wium Lie 和 Bert Bos。CSS 1 提出的"样式与结构分离"理念是对 HTML 表格布局乱象的有力反击——1996 年之前,网页布局完全依赖嵌套表格,代码膨胀到难以维护。CSS 提供了层叠(Cascading)机制,把作者样式、用户样式、浏览器默认样式按优先级合并,这是一种天才的民主化设计:终端用户可以用用户样式表覆盖网站配色,残障人士可以强制加大字号。今天我们习以为常的盒模型(box model)就是 CSS 1 奠定的。然而 IE 和 Netscape 都对 CSS 1 做了不同程度的扩展,IE 的 `filter`、Netscape 的 `<layer>` 形成了著名的"DHTML 噩梦"——同一段代码在两台浏览器上跑出完全不同的结果。
微软的反扑从 1995 年 8 月开始:Windows 95 Plus! Pack 捆绑了 IE 1.0,1997 年 IE 4.0 发布时微软直接集成进 Windows 操作系统。这一策略被欧盟在 1998 年起诉,2009 年达成和解。1996—2000 年间,IE 的市场份额从 20% 飙升至 95%。Netscape 在 1998 年 11 月被 AOL 以 42 亿美元收购,但大势已去;1998 年 1 月 Netscape 开放了源代码成立 Mozilla 项目,2002 年发布 Mozilla 1.0,2004 年改名 Firefox 1.0。IE 6(2001 年 8 月) 是这段历史的最高峰,也是最深的低谷:它统治了 2001—2006 年的浏览器市场,但微软在 2001 年发布 IE 6 之后长达 5 年没有重大更新,几乎冻结了 Web 标准演进。
1.2.2 第二次浏览器战争(2004—2017):Web 2.0 与多极化
2004 年是转折之年。Mozilla Firefox 1.0 发布并以"开放、可扩展、安全"为卖点迅速回血。Apple 在同年 1 月发布了基于 WebKit 的 Safari 1.0(WebKit 本身来自 Linux 桌面的开源项目 KDE 的 KHTML 引擎)。同年,WHATWG(Web Hypertext Application Technology Working Group) 由 Apple、Mozilla、Opera 三家成立,正式向 W3C 的 HTML 演进路线发出挑战。
2005 年 2 月,Jesse James Garrett 在一篇文章中创造了AJAX(Asynchronous JavaScript and XML) 一词,标志 Web 2.0 时代正式到来。AJAX 不是新技术——`XMLHttpRequest` 对象在 IE 5(1999 年)就已存在——但 Google 的 Gmail(2004 年 4 月)、Google Maps(2005 年 2 月)证明了"异步通信 + DOM 操作"可以构建桌面级应用体验。这触发了 2005—2010 年的 JavaScript 框架爆发期:Prototype、Dojo、ExtJS、jQuery(2006 年 8 月)相继出现。jQuery 用"写更少、做更多"的口号统一了浏览器 API 的差异,它的链式调用、隐式迭代、Sizzle 选择器引擎成为事实标准。
2008 年 9 月,Google 发布 Chrome 浏览器,内核是 WebKit 的分支 Blink 的前身(Blink 在 2013 年 4 月独立)。Chrome 的颠覆性创新不在 UI,而在两点:多进程架构——每个标签页独立进程、独立的 V8 JavaScript 引擎实例、独立的渲染进程,让一个页面的崩溃不会拖垮整个浏览器;V8 引擎——它率先把 JIT(Just-In-Time)编译 引入主流浏览器 JavaScript 引擎,把字节码直接编译为机器码而不是解释执行,再配合隐藏类(hidden class) 和内联缓存(inline caching) 优化对象属性访问。V8 让 JavaScript 的执行速度从 2008 年到 2015 年提升了 20 倍以上,为后来的 Node.js、Electron、React Native 奠定了基础。
微软的反应迟缓而致命:IE 7(2006 年)只是小幅修补,IE 8(2009 年)开始支持 CSS 2.1 但依旧拖后腿,IE 9(2011 年)支持 HTML5 但仍落后一代,IE 10(2012 年)和 IE 11(2013 年)才勉强赶上主流。直到 2015 年微软推出基于 EdgeHTML 的 Edge,2018 年宣布放弃自研内核转向 Chromium,2020 年正式发布基于 Chromium 的 Edge。IE 6—IE 11 的拖累,直接造成 2001—2015 年间全球前端工程师必须编写大量 hack 代码,这是 Web 标准史上最大的遗憾。
1.2.3 第三次浏览器战争(2017—至今):引擎三分与 Chromium 霸权
今天的浏览器内核市场已经被三个引擎垄断:Blink(Chromium 内核,Chrome/Edge/Opera/Samsung Internet)、Gecko(Firefox)、WebKit(Safari)。Chromium 因其开源、高度工程化、强大的开发者工具生态,已经占据桌面浏览器 70% 以上的市场份额。这种近乎垄断的局面引发了新一轮标准博弈——WHATWG 在 2019 年与 W3C 达成协议,HTML 标准由 WHATWG 独家维护,W3C 退出 HTML 编辑工作;CSS 规范则进入多工作组并行模式(CSS Fonts、CSS Grid、Flexbox、Houdini 等),CSS 工作组由 Apple、Google、Microsoft、Mozilla、Igalia 等公司轮流主导。
浏览器厂商之间的竞争并未消失,只是战场转移到了性能指标(如 Core Web Vitals)、隐私保护(如 Intelligent Tracking Prevention、Total Cookie Protection)、新特性首发权(如 WebTransport、WebGPU、WebNN)三个维度。Web 标准的真正考验不再是"谁首先实现",而是"如何避免一家独大导致创新停滞"——这是 WHATWG 当前面临的最大治理挑战。
1.3 从 W3C 到 WHATWG:Living Standard 的范式转移
在浏览器战争的硝烟背后,还有一场更隐蔽但更深远的战争:Web 标准的治理模式之争。W3C 从 1994 年成立到 2010 年代初,一直采用"快照式(snapshot)"标准制定流程:工作组经过数年的争论、修改、反复讨论,最终发布一个带版本号的规范(如 HTML 4.01、XHTML 1.0、CSS 2.1),然后进入"完成"状态,浏览器各自去实现。这种模式适合 SGML、XML 时代的"稳定后发布"理念,但它有一个致命缺陷:Web 平台正在被使用,不能停下来等待标准。
2004 年,WHATWG 的成立本身就是对 W3C 的反叛。当时 W3C 正在推动 XHTML 2.0——它要求 HTML 必须采用严格的 XML 语法:所有标签必须闭合、属性必须加引号、标签必须小写、文档必须有完整的命名空间。XHTML 2.0 还打算抛弃与 HTML 的兼容性——`<a>` 标签不再直接表示链接,需要用 `<ref xlink:href="...">`。这个提案激怒了浏览器工程师,因为全世界已经存在的数十亿个网页都将失效。WHATWG 在 2004 年由 Ian Hickson(Opera 的工程师,后来加入 Google)主导,开始编写一份不设版本号、不承诺兼容性、随时滚动更新的规范——HTML5 的雏形。
2008 年 1 月,HTML5 第一份公开草案发布;2009 年 W3C 宣布 XHTML 2.0 项目终止、HTML5 立项;2012 年 12 月 WHATWG 与 W3C 达成协议:W3C 继续发布 HTML5 候选推荐(CR),WHATWG 继续维护Living Standard;2014 年 10 月 W3C 发布 HTML5 推荐标准;2019 年 5 月 W3C 与 WHATWG 签署新协议,HTML Living Standard 由 WHATWG 独家维护,W3C 参与审阅但不再发布新版本。
Living Standard 的本质是一个范式转移。它把 Web 标准从一个"产品发布"的过程,变成了一个"持续演化"的过程。新特性通过"提议 → 浏览器实验 → 实现反馈 → 规范修正"循环演进,浏览器实现与规范文本同步更新,规范永远处于"工作进行中(Work In Progress)"状态。这种模式的优势是快速迭代——CSS Grid(2017)、CSS Subgrid(2019)、Container Queries(2022)、`:has()` 选择器(2023)从实验到正式标准的周期从过去的 5—8 年缩短到 2—3 年;劣势是不可预测的兼容性——开发者必须随时关注 `caniuse.com` 来确认目标浏览器的支持度。
HTML5 的另一项革命是语义化(semantics)。它引入了 `<article>`、`<section>`、`<nav>`、`<aside>`、