当年只需要知道 HTML、CSS、JavaScript,以及一些 jQuery 的知识,就可以在前端开发的世界中游刃有余。
而如今,我们面对的是 React、Vue、TypeScript、Node.js、Next.js、 shadcn/ui 等各种框架和工具的洪流。
学习成本直线上升,这其中到底发生了什么?
今天我们就从这个问题开始,聊聊 JavaScript 框架的发展过程,了解背后的故事。
前端开发 2016
前端开发 2024
1.JavaScript:一种编程语言的诞生
在讲述 JavaScript 框架的发展之前,先简要回顾一下 JavaScript 的历史,帮助理解为什么现代前端开发如此混乱。
网景通信公司是一家美国技术公司,开发了第一个广泛使用的网络浏览器——网景导航器(Netscape Navigator)。
1995 年,网景聘请了 Brendan Eich,希望将 Schem e编程语言集成到其网景导航器中。
然而,Eich 在加入公司仅 10 天后就创造了一种新的编程语言,起初命名为 Mocha,后改为 LiveScript,最终确定为 JavaScript。
尽管名称暗示了与 Java 的关联,JavaScript 实际上与 Java 编程语言关系不大。这一命名更多是出于市场营销策略,因为当时 Java 正变得越来越受欢迎。
到了 1990 年代末,网景试图通过 Ecma International 来标准化 JavaScript,这一努力最终导致了 ECMAScript 标准的诞生。
尽管 JavaScript 的发展历程中有过不少波折,如与微软的浏览器大战,但它经受住了时间的考验,成为了世界上最流行的编程语言之一。
1999 年,网景被美国在线(AOL)收购,该产品于 2008 年停止更新和支持。
2.浏览器大战
为了理解现代前端开发背后的混乱,我们需要回到 90 年代中期到 2000 年代初。
那时候,关于浏览器的选择并不多。你要么使用网景/火狐,要么使用微软的 IE,前者在市场上占有绝大多数份额,高达90%。
Safari 和 macOS(最初称为Mac OS X)当时几乎被开发者忽视,我记得 Safari 在 2000 年代初的市场份额不超过 0.5%。
在盖茨的领导下,微软展现出了极强的竞争性,简单地说就是消灭了所有竞争对手。
对微软来说,浏览器与其他商业产品无异,用户必须购买这些应用程序并将其安装在个人电脑上。
微软想要独占所有市场份额,因此他们的做法很简单:将 IE 作为默认浏览器与 Windows 一同发布,这样当用户“打开互联网”时,映入眼帘的便是 IE。
作为回应,网景尝试将他们的浏览器与 ISP 提供的软件捆绑。然而,作为市场巨头的微软则要求所有第三方应用必须捆绑 IE,将其作为“Windows 上进行网络访问的必需组件”。
微软在处理 IE 中的脚本语言时无视任何标准,完全按照自己的方式行事,不在乎 Web 标准。
接着,微软对 IE 中的脚本语言为所欲为,无视其他供应商试图制定的任何标准。在微软看来,非我族类,其心必异。
如果按照微软的方式,我们今天可能都在使用 IE 和 VBScript(尽管我认为 VBScript 在当时是一种比 JavaScript 更有能力的脚本语言)。
他们甚至还逆向工程了网景的脚本语言 JavaScript,并将其命名为 JScript。
最终,微软因垄断网络浏览器市场而被判有罪,裁定操作系统和浏览器必须分离,用户应有权选择使用任何他们想要的浏览器。
在 VBScript、JScript 和 JavaScript 之间,最终胜出的是 JavaScript,因为开发者可以使用同一种脚本语言为多个浏览器进行开发。
但曾有一段时间,网页代码中 JavaScript 和 VBScript 混用,造成了巨大的混乱。
注:还存在许多其他脚本语言,如 PerlScript 或 ASP。这与今天 WebAssembly 试图解决的问题类似,即允许开发者在客户端使用任何编程语言,同时确保性能和跨浏览器的兼容性。
3.jQuery 的诞生
在 2000 年初,Web 世界还像是一个乱世,存在许多相互竞争的标准,厂商们还在摸索如何有效地为Web编程。
如果将今天的前端开发描述为复杂,那么那时它更是混乱无章,寻求帮助的在线教程寥寥无几。
正是在这种背景下,jQuery 应运而生。它的初衷是为开发者提供一个统一的、能够跨浏览器工作的 API。
在 jQuery 出现之前,如果你想处理 JS 事件,你需要为几种不同的浏览器标准编写不同的代码,以确保跨浏览器兼容。
以下是一个展示 2005 年网络开发难题的简短代码示例:
function myEvent() {
alert('click event');
}
// 检查浏览器是否支持 addEventListener 方法。
if (elem.addEventListener) {
// 为元素添加点击事件监听器,使用标准的方法。
elem.addEventListener("click", myEvent, false);
} else if (elem.attachEvent) {
// 为元素添加点击事件监听器,用于不支持 addEventListener 的老旧浏览器(如IE8及以下版本)。
elem.attachEvent("onclick", myEvent);
}
jQuery 的实施效果非常好,它通过简洁强大的 API,解决了跨浏览器的兼容问题,使开发者无需操心这些问题。它的创新之处在于,它以浏览器的功能而非用户代理为目标,这一策略极大地改变了游戏规则。
上面的代码可以用 jQuery 重新编写,无需考虑浏览器兼容问题:
$(function() {
$('#elem').click(function() {
alert('click event');
});
});
我记得第一次在 QuirksMode 上读到关于 jQuery 的文章时,对其简单的使用方式感到震惊。我只需添加一个脚本标签,一切问题即刻迎刃而解,仅需一行代码。
jQuery 迅速成为 Web 开发者的新宠,广泛应用并推动了我们今天热爱并使用的许多现代 Web 标准的发展。
我们不应忘记,尽管有人认为 jQuery 的流行主要得益于 WordPress,但 jQuery 仍是当今使用最广泛的 JavaScript 框架之一。
在解决了浏览器 JavaScript 兼容性问题之后,jQuery 的下一步发展是其插件系统。
为了保持核心库的精简,jQuery 设计了一个插件系统,允许开发者扩展其功能。开发者们借此创建了众多可复用的UI组件,例如著名的 jQuery 日期选择器。
4.Backbone.js 与 AngularJS 登场
Backbone.js 于 2010 年问世,它是最早为客户端 Web 应用带来结构化设计,并迈向单页应用(SPA)的先锋之一。
一些开发者认为 Backbone 的重要贡献在于促进了前端开发理念的转变,虽然不能忽视 YUI 库在这方面的早期尝试。
在 Backbone.js 出现之前,网站主要通过服务器端应用推送 HTML 和 CSS 到客户端浏览器,并通过 JavaScript 来“渐进式”增强用户体验,这就是“渐进增强”的由来。
随着网站交互性的增强,需要在客户端维护一个与服务器端和 DOM 同步的数据模型。Backbone.js 通过其基于 MVC(模型-视图-控制器)的架构设计来满足这一需求,这标志着基于组件的 Web 开发的一个重要里程碑。
另一方面,AngularJS 作为谷歌的创新产品,首发于 2010 年,其后续版本 Angular 2+ (从2016年开始,统称为Angular)不同于 Backbone.js,它不是一个库。
Angular 是一个功能全面的 JavaScript 框架,它引入了“指令(directives)”这一概念(后被 Vue 采用),提供了一种扩展 HTML 语法的优雅机制,允许开发者创建自定义 HTML 元素,这在基于组件的开发中是一个划时代的概念。
通过引入“指令”,Angular 实现了可重用组件的创建,显著提高了代码的维护性和可读性,正如其市场宣传所述。
总的来说,Backbone.js 和 Angular 都在 Web 开发社区中引发了关于如何创建更高效、可维护和可扩展的客户端应用的重要讨论,并推动了基于组件的 Web 开发的热潮。
5.利用 CoffeeScript 进行转译
CoffeeScript 在 JavaScript 框架演进中扮演了关键角色,主要体现在它引入了“转译”的概念,即源到源的编译方式。CoffeeScript 的作者也是 Backbone.js 的开发者。
对于那些厌倦了处理 JavaScript 的复杂性和各种特殊情况的开发者来说,CoffeeScript 提供了一个解决方案:创造一种全新的语言,然后将这种语言转译成 JavaScript,使其能够在浏览器中运行。
比如,JavaScript 中的作用域和闭包常常让新手和经验不足的开发者感到困惑。尤其是 var 关键字导致的变量提升问题,以及函数作用域与块级作用域的差异。
CoffeeScript 引入了 let 和 const,这两个关键字帮助定义块级作用域变量,减少了由于作用域引起的常见错误。
除了提供更简洁的语法外,CoffeeScript 引入的高级功能,如解构赋值、类和数组推导,极大地简化了 Web 组件的构建过程。这些特性最终被纳入现代 JavaScript 中,对 JavaScript 的发展产生了深远的影响。
6.Node.js,JavaScript 的颠覆之路
Node.js 可以视为一股助燃剂,点燃了客户端技术快速发展的潜行之火,推动了今天我们使用的多种现代 JavaScript 标准的广泛采纳。
在 Node.js 出现之前,JavaScript 主要是一种客户端语言,用来增强网页的交互性。
Web 开发者在 JavaScript 开发工具方面选择有限,大多依赖于浏览器的运行时执行,并常常需要借助其他服务器端技术手动进行代码的压缩和合并。
Node.js 的诞生使得 JavaScript 得以跨越到服务器端,允许开发者在浏览器环境之外执行 JavaScript 代码。
尽管 Node.js 的初衷是展示 JavaScript 的通用性,并承诺能够简化 Web 开发流程、优化代码、捕获错误,并通过在服务器和客户端使用同一编程语言来自动化重复性任务,但实际上它触发了相反的趋势。当然,这种趋势的形成并非 Node.js 所能控制。
Node.js 提供了开发各种工具的平台,催生了包括 Grunt、Gulp、Webpack、Babel 等在内的丰富的包生态系统。
毫无疑问,Node.js 的另一个重要创新是其包管理器 NPM,该管理器极大地简化了 JavaScript 库和工具的安装与分发过程,为 JavaScript 的扩展和应用提供了极大的便利。
7.React 和前端工具的疯狂演进
我们已经进入了单页应用(SPA)、互动丰富的 Web 应用领域,React 在这里无需过多介绍了吧。
虽然 React 并未引领我们今天前端开发复杂的生态系统,但它确实改变了我们的开发方式,使之更加偏向于 React 的专有方式。
随着对创建高交互性和高性能 Web 应用的需求不断增加,围绕模块化JavaScript 代码开发了越来越多的工具。这种对技术的追求在一定程度上塑造了我们今天所见的开发环境。
我第一次接触 React 是在 2014-2015 年,客户的技术领导完全接受了 React 早期的 Flux 架构。
当时我强烈反对使用 JSX,这反映了更广泛的 Web 开发社区的看法:“哪个理智的开发者会把 HTML 嵌入JavaScript中?!”
但现实就是这样,HTML 最终被嵌入到 JS 中,接着是 CSS,像 Web 开发中许多事情一样,你不得不逐渐接受它。
现在,管理和响应浏览器中的应用状态并对其作出反应,已成为主流。
React 开启了这一变革,Vue 则继续推动这种以 JavaScript 为中心的开发转变,鼓励开发者使用 JavaScript 编写一切,包括标记语言和样式,并提倡“一次创建,随处使用”的组件理念(至少这是最初的承诺)。
React 通过 React Native 则更进一步,承诺可以在包括移动端在内的任何平台上使用一次编写的代码。
React 还加剧了前端开发中的反馈循环趋势,随着越来越多的工具和 JS 模块被创建出来,为了在进一步改进或修复广泛使用的主要 JS 框架的不足,这些框架的使用往往未经过深思熟虑。
注:如果你有时间,不妨观看 React.js 的纪录片,其拍摄和制作质量都很高。
React.js 纪录片:https://www.youtube.com/watch?v=8pDqJVdNa44
8.TypeScript,复杂类型的系统修补
与 CoffeeScript 相似,TypeScript 是一种源到源的编程语言,并非 JavaScript 框架,但它在这些框架的演进中起到了关键作用,这一点得到了微软和无数开发者的认同。
编程语言中的类型安全极为重要,但我对于在本质上应支持动态类型的 JavaScript 上强加静态类型持保留态度,毕竟这不应视为缺陷。
在这方面,我赞同 DHH 对 TypeScript 的批评,他指出了TypeScript的局限性,解释了为什么他们的产品中避免使用它。
DHH 对 TypeScript 的批评:https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01
在 TypeScript 出现之前,JavaScript 以其在大规模项目中的难以管理而闻名,这不仅仅是因为缺少静态类型。
然而,静态类型的缺失并没有让 JavaScript 更出色,反而随着项目的扩大,调试和维护代码的复杂性也在增加。
TypeScript 引入了如接口、枚举类型和可选参数等强大功能,这些特性对于管理复杂的 JS 项目至关重要。
此外,TypeScript 还提供了开发工具支持,如更准确的自动完成、重构能力等功能,大大提高了开发效率(同时,自动完成和 lint 规则也可以通过 JSDoc 设置)。
但是,TypeScript 本质上是在 JavaScript 之上增加了一层,带来了可能并非对每个项目或开发团队都必要或适宜的复杂性和开销。
尽管 TypeScript 提供了许多增强功能,但学习一种全新的语言及其语法、规则和最佳实践的代价也不小。这对于小型项目或团队来说,可能是一个不小的挑战。
TypeScript 要求开发者为变量、函数返回值和参数注明类型。虽然这在大型项目中有助于提高代码的可读性和调试性,但在小型项目中可能会拖慢开发速度。
总之,TypeScript 引发了关于在 JavaScript 中引入可选静态类型的讨论,促进了语言的成熟,这无疑是一件好事。
9.Svelte,为前端开发带来一丝光明
随着前端开发者对 JavaScript 生态系统的普遍疲劳,Svelte 应运而生,带来了新的活力。
Svelte 试图解决传统框架导致的代码膨胀问题。到目前为止,React、Angular 乃至 Vue 都已被视为传统框架。
Svelte 还试图降低学习难度,其他框架和库的复杂性往往使初学者望而却步。与之形成对比的是,Svelte 的设计宗旨是直观易懂。
通过简化的语法和减少的样板代码,即使是只有基础 HTML、CSS 和 JavaScript 知识的开发者也能在较短时间内掌握 Svelte。
Svelte 也可以看作是一种编译类 HTML、CSS 和 JS 代码的编程语言,而非解析常规 HTML/CSS/JS 的解析器。
如果把 Svelte 视为一种编程语言,那么我们写的代码虽然看起来像 HTML、CSS 和 JS,但实际上并非如此。
这就是 Svelte 为什么被归类为 devDependencies 的原因,因为与 React 等不同,我们在生产环境中不需要 Svelte 的代码。
通常,在使用传统 JavaScript 框架的情况下,客户端浏览器需要加载并执行整个 JavaScript 包,用户才能与应用互动。
这个过程可能很慢,尤其在移动或网络慢的环境下,可能会对用户体验产生不利影响。SvelteJS 通过在构建过程中完成繁重的工作来解决这个问题。
与 React 不同,Svelte 在如何处理事务上持有更明确的立场,这在 SvelteKit 中表现得尤为明显。
此外,Svelte 是首批开始探讨多页应用(MPAs)在客户端开发中地位的主要框架之一。在此之前,MPAs 被视为过时的开发方法,不再被开发者采用。
尽管如此,Svelte 仍在前端开发的复杂局势中尝试带来一丝光明。
10.Htmx,前端开发的新视角
如果你还不熟悉 htmx,那也无妨。它是一个较新的 JavaScript 框架,更准确地说,它是对现代前端开发的一种全新思路。
尽管 CoffeeScript、TypeScript 和 Svelte 试图通过使用类似 JavaScript 的语言来修复 JavaScript 的缺陷,htmx 的开发者则选择了一条不同的道路——从零开始,完全不使用 JavaScript。
Htmx 使得 Web 开发者能够直接在 HTML 中使用 AJAX、CSS Transitions、WebSockets、JS Events 和 SSEs(服务器推送事件),无需编写大量的 JavaScript。
这种方法虽然不是全新的,但它以一种无缝的方式将静态 HTML 转化为动态内容,为 Web 应用增强提供了新的途径。
与其他复杂的 JavaScript 框架不同,htmx 提倡一种更简单的 Web 开发方法,通过在 HTML 中直接实现动态 UI 交互,显著减少了对 JavaScript 的依赖。
这种方法强化了 Web 开发中的渐进增强原则,即使用 HTML 和 CSS 构建基础和布局,而将 JavaScript 保留为增加必要交互性的轻量级工具。
Htmx 的简洁性可能令人不安,我们曾经深陷前端开发的复杂中不得自拔,而 htmx 提醒我们,许多它所采用的技术实际上早已存在,只是被我们忽视了。
通过后端语言的应用,开发者可以在后端保持数据和状态,而前端则保持 UI 的动态性和响应性,这才是最理想的分工。
这种回归简洁的趋势对年轻开发者而言是一件好事,他们将发现在不依赖现代 JavaScript 框架的情况下进行前端 Web 开发的乐趣。尽管如此,这并不意味着 React、Vue、Svelte 和 Angular 会很快消失,我们需要循序渐进地接受这一变化。
全栈开发者的归来预示着前端开发领域正在迎来更全面的技能要求和新的发展机会。
11.未提及的重要技术
我知道我遗漏了许多在现代前端开发中具有重要影响的技术,比如 Alpine、Astro、Babel、Bun、Dojo、EmberJS、Esbuild、ES6、ExtJS、Express、Gatsby、GraphQL、Jamstack、KnockoutJS、Lit、MeteorJS、MERN、MooTools、Next、Nuxt、Parcel、Polymer、Preact、PWAs、Remix、REST、RollupJS、Rome、Ruby on Rails、RxJS、SproutCore、Sass、Skypack、Snowpack、SolidJS、Stencil、Stylex、Vite、Vue、Zig 等等。这些技术在现代前端开发中都以某种方式做出了贡献。
主要原因有:1) 我无法一一涵盖所有内容,2) 很多技术有相似的理念,最后 3) 我对其中许多技术不够熟悉,难以做出深入的评价。