JavaScript 框架历史(译)

2023-03-26 • ☕️☕️☕️☕️ 20 min read

本文是我尝试翻译的文章,限于自身水平,如有错误,请读者们直接指出,感激不尽~ 原文也有电子书格式,如果大家愿意对原文作者的付出进行支持,可在直接购买

前言

JavaScript 生态系统中有许多框架和库。有人说我们正处于 JavaScript 的文艺复兴时期,一个充满创新且快速发展的时代,旧工具不断被新工具所取代。我们不断推进 JavaScript 应用程序的极限,这是一个充满活力的时代。

然而,从另一方面来看,许多人对 JavaScript 庞大的生态系统感到疲惫(学不动了)。此外,在 GitHub 的某个角落可能还存在着一个你从没有听过的、收藏过万的框架。

但是为什么我们有这么多框架,它们的目的是什么?为了更好地理解现在,我们可以回顾过去。本书将带你踏上从 90 年代至今的旅程。在我们的旅行中,我们会遇到许多框架和库。这些相遇将增加我们对它们的模式、故事、使用案例等方面的了解。

我们的使命很明确。是为了探索 JavaScript 框架的历史。

JavaScript 框架的诞生

JavaScript 创建于 1995 年,它使我们有了让网页变得生动的能力。有了 JavaScript,我们可以响应用户事件、执行动画、验证输入等等。我们使用术语dynamic HTML (DHTML) 来描述这个概念。然而,神奇的 DHTML 在 90 年代并没有得到真正的利用,因为平台的不兼容,编写兼容于多种浏览器的 JavaScript 很麻烦。平台的混乱源于“浏览器大战”造成的灾难。网景(Netscape)和 微软(Microsoft)公司故意通过为各自的浏览器创建新特性来吸引开发人员的兴趣,从而相互竞争,这随后导致了平台不兼容,或者更准确地说,是一个雷区。结果导致开发者必须仔细确保他们的代码可以在用户选择的任何浏览器上运行。

幸运的是,网景和微软在 1997 年创建了 JavaScript 语言规范,并将其提交给了 ECMA(ECMA International)。ECMA 是一个标准化组织,直到今天还在发布 ECMAScript 版本。所以从 ECMAScript 成为语言的标准规范后,就有了这么一个说法,JavaScript 就是 ECMAScript。语言中的新特性被持续提出,并在更新的 ES 版本 (ECMAScript 的缩写) 中发布。这就是为什么你看到关于 ES3、ES5、ES6 等的文章的原因。由于浏览器使用它们自己开发的引擎来实现 JavaScript,它们是否遵循规范就取决于它们自己。

浏览器大战也终于落下了帷幕。微软通过免费赠送 Internet Explorer 4 并将其预装在 Windows 操作系统中的策略,无情地击败了网景,最终成为浏览器大战的胜利者。胜利的奖励是 Internet Explorer 在多年里几乎完全主宰了浏览器市场。因此,从 2001 年到 2006 年,只发布了一个新的 Internet Explorer 版本,创新就此停滞。尽管动荡不断,但最终还是迎来了稳定,这使得更多的开发者可以尝试使用 DHTML。

但仍有挑战需要克服,因为 JavaScript 几乎没有被开发者社区热情拥抱。JavaScript 被嘲笑并被视为开发者只在需要添加一些 GUI 组件或客户端验证时才会使用的低级语言。然而,经过多年的发展,JavaScript 已经发生演化并在 Web 开发领域有广泛的应用。即使到了今天,尽管它获得了许多荣誉,但仍然为许多人所鄙视。对 JavaScript 的强烈反感有时源于合理的理由,但有时也源于缺乏理解。诚然,即使是 Brendan Eich 在设计 JavaScript 时也希望使这种语言易于理解,并将 Java 或 C++ 视为“专家”的语言。

尽管如此,当公司开始尝试一系列网络技术时,一个新时代的曙光即将到来。自 20 世纪 90 年代初以来,网站就被分为网页。每当用户请求查看网页时,就需要浏览器加载整个页面。因此,浏览网站的体验不如使用桌面应用程序那么流畅。这个问题不仅发生在导航期间,而且在向服务器发送数据时也会发生。例如,当用户提交填写好的表单到服务器时,用户将不得不等待服务器返回整个确认页面。这种糟糕的体验促使开发者尝试不同的技术,来让网站的表现更像桌面应用程序。

奇怪的是,早在 1999 年,微软就已经创造了可以解决这个问题的技术。微软的 Outlook Web Access 团队已经可以设法通过客户端脚本发送 HTTP 请求。这使得通过客户端从服务器请求数据,然后客户端可以无需重新加载页面即可动态更新页面的一部分成为可能。由于这些请求是在后台处理的,因此用户可以向服务器发出请求,同时仍然可以与网站进行交互。随后该技术被添加到 Internet Explorer 5.0 中。后来该请求被称为 XMLHttpRequest,可以通过 JavaScript 发起该请求。然而,它花了六年的时间才被公众广泛知晓。

大约在 2005 年,JavaScript 受到了大量的关注,当时一位名为 Jesse James Garrett 的 UI 设计师注意到了一些令人印象深刻的新网站,例如 Gmail、Google Maps 和 Flickr。这些网站利用 XMLHttpRequests 和一系列技术来创建类似桌面的应用程序。对于这个 Web 技术集合,Garrett 创造了术语 Asynchronous Javascript + XML (AJAX)AJAX 使用户能够从服务器请求数据而无需重新加载页面。因此,开发人员可以使用 DHTML 来使用从服务器响应获取的数据来更新显示。尽管这些技术已经存在了一段时间,但这一切都引起了巨大的觉醒。这种觉醒促使开发人员继续努力突破 Web 应用程序的局限性。

尽管主流浏览器仍然存在兼容性问题,开发者们的梦想和雄心使得项目越加复杂,因此一些框架/库或所谓的 AJAX 框架 开始走向主流。一些流行的有:

  • Prototype.js
  • Dojo
  • Mootools
  • Yahoo! User Interface Library (YUI)

这些框架旨在解决以下问题:

  • 简化 AJAX 和动态加载
  • 处理跨浏览器事件
  • 遍历和操作 DOM
  • 浏览器不兼容

正如我们所看到的,2000 年至 2005 年期间已经有很多框架可供选择。你可以通过 Web 档案访问旧的 ajaxpatterns.org 站点来亲自查看。现在这些框架大部分都已经消失了,但是有一个库从那个时代一直存活到现在。你可能听说过它,当然就是 jQuery。

jQuery 发布于 2006 年 1 月,作者 John Resig 是想在浏览器中更简单的使用 JavaScript。他通过专注试验不同的 DOM 操作工具,同时考虑浏览器的兼容性,最终把它们融合进了一个库——就是 jQuery。后来应用户需求,又加入了 AJAX 相关的方法。

这个库的成功受到大量关注,它让使用 JavaScript 变得更简单,也促使开发者创造更大的项目。但是这也同时引入了更多的复杂性和一系列项目维护的问题。这些项目由于包含了大量 spaghetti code(面条式代码)变得难以维护。种种维护性问题表明像 jQuery 这样的库不足以解决开发人员当时想要完成的任务。还没有明确的方法解决如何组织和构建代码的问题。

所以就出现了要降低项目复杂性、项目可维护的需求。结果就是,新的尝试解决维护性问题的 JavaScript 框架开始出现。

MV* 框架的兴起

2010 年,Knockout.js、Backbone.js 和 AngularJS 加入了进来。它们都属于 MV*(Model View Everything)家族,这意味着它们采用了 MVC 的概念。

Model-view-controller (MVC), 其中,Model(模型)包含逻辑,View(视图)是模型的表现,Controller(控制器) 是用户和系统之间的链接。

MVC.png

通过 Model–view–presenter (MVP),用户与视图层(View)交互,将职责委托给呈现层(Presenter),呈现层在视图层与模型层之间,模型层可以触发事件,呈现层可以监听事件并做出相应反应。

MVP.png

使用 Model–view–viewmodel (MVVM),视图层(view)和视图模型层 (viewmodel) 自动进行绑定。用户交互通过数据绑定转发到视图模型层,模型层本身仍然包含业务逻辑或数据。

MVVM.png

最后,我们需要了解框架和库之间的关键区别。Backbone.js 和 Knockout.js 是库,而 AngularJS 是框架。因此,这意味着 AngularJS 对你的应用程序的架构持有强烈的约束。而 Backbone.js 和 Knockout.js 通过让你承担更多责任,在这方面为你提供了更多自由。

MV* 框架的历史

2010 年 7 月,Knockout.js(简称 KO)发布。它是由 Steve Sanderson 创建的库,他也制作了 Blazor 的第一个版本。Knockout 的基础是 MVVM 设计模式,该模式通过 observables 和 bindings 来实现。

绑定 HTML 元素到 observables,然后 Knockout 就能观察到视图模型(ViewModel)的属性变化。元素上的任何变化都会反应到它们的模型上,反之亦然。这样我们实现了双向绑定。

<script>
  const viewModel = {
    price: ko.observable(10),
    increasePrice = () => { this.price++; };
  };
 
  ko.applyBindings(viewModel);
</script>
 
<p>The price is <span data-bind="text: price"></span></p>
<button data-bind="click: increasePrice">Increase the price</button>

Steve Sanderson 从未打算取代 jQuery ——事实恰恰相反。Sanderson 预计 Knockout 和 jQuery 可能会一起使用。他的想法是避免复杂的事件处理程序,并为我们提供一个绑定到用户界面的连贯的底层数据模型。此外,它还包括一些实用的工具函数,例如数组过滤和 JSON 解析。总之,Knockout 是一个允许我们使用双向绑定并保持我们自由设计项目架构的库。

2010 年 10 月,Backbone.js 由 CoffeeScript 的创造者 Jeremy Ashkenas 发布。Backbone.js 构建应用程序的方法类似于 MVP 模式。MVP 模式可以通过使用 Backbone.Model 来实现 M,模板(如 Underscore/Mustache)用于 V,最后,尽管名称为 Backbone.View,但它实现了 P。

与其他使用声明式方法更新 DOM 的框架和库不同,Backbone.js 使用更为命令式的方法。例如,请查看下面 Backbone.Viewrender 方法内部。它包含一个强制更新 DOM 的选择器(尽管模板可以说是声明性的)。换句话说,Knockout 只能专注于模型,而使用 Backbone,你必须手动更新 DOM。

<div id="price-container"></div>
 
<!-- 视图层 -->
<script type="text/template" id="price-template">
  <p>The price is <%- price %></p>
  <button id="priceBtn">Increase the price</button>
</script>
 
<script>
  // 模型层
  const PriceModel = Backbone.Model.extend({
    defaults: {
      price: 10,
    },
  });
 
  // 呈现层
  const PriceView = Backbone.View.extend({
    el: "#price-container",
    template: _.template($("#price-template").html()),
    events: {
      "click #priceBtn": "increasePrice",
    },
 
    initialize() {
      this.listenTo(this.model, "change", this.render);
      this.render(); // 在创建时渲染
    },
 
    render() {
      const valuesForTemplate = this.model.toJSON();
      this.$el.html(this.template(valuesForTemplate));
 
      return this;
    },
 
    increasePrice() {
      this.model.set("price", this.model.get("price") + 1);
    },
  });
 
  const priceModel = new PriceModel();
 
  // 创建视图 (View) 开始
  new PriceView({ model: priceModel });
</script>

大约十年前,Jeremy 在一次有关 Backbone 的演示中宣称,简而言之,Backbone 的目标是提供构建 JavaScript 应用程序时所需的组件,其核心是一种利用模型和视图来处理数据和 UI 的方法。此外,Backbone 提供了比原生 JavaScript 更好用的操作方法。除此之外,它还可以通过 Backbone.Router 将应用程序的位置映射到 URL 来实现客户端路由。所以 Backbone 库为你提供了一系列工具,用于构建小型或大型应用程序。

2010 年 10 月,AngularJS 框架发布。AngularJS 是由 Miško Hevery 和 Adam Abrons 发起的一个业余项目。AngularJS 的基础是通过视图、模型和控制器实现的 MVC 模式。对于视图,AngularJS 通过向称为指令的元素添加前缀为 ng- 的属性来扩展 HTML 的功能。指令提供了一种将 HTML 绑定到 JavaScript 对象的便捷方法,但在当时,它引发了我们是否真的应该扩展 HTML 的争论。

尽管如此,开发人员仍然对 AngularJS 提供的双向数据绑定印象深刻。因为要控制数据,你需要通过将 ng-controller 指令添加到元素来创建附加到 DOM 的 controller。之后,controller 接收到一个$scope 对象 (表示模块),它将视图和 controller 粘合在一起。因此控制器可以只关注模型。

<script>
  const app = angular.module("priceApp", []);
  app.controller("priceController", ($scope) => {
    $scope.price = 10;
    $scope.msg = "hello";
    $scope.increasePrice = () => $scope.price++;
  });
</script>
 
<div ng-app="priceApp" ng-controller="priceController">
  <!-- 单向绑定 -->
  <p>{{msg}}</p>
  <p>The price is {{price}}</p>
  <!-- 事件绑定 -->
  <button ng-click="increasePrice()">Increase the price</button>
  <!-- 双向绑定 -->
  <input ng-model="msg" />
</div>

据说,Miško 曾在谷歌工作,在「谷歌反馈」的项目中投入了有六个月,积累了超过 17000 行代码。测试和添加新功能变得越来越困难。Miško 认为可以用 AngularJS 在两周内重写项目,当时的谷歌经理 Brad Green 同意了,并和 Miško 打赌。结果是 Green 赢了,因为 Miško 用了三个星期。

比短暂的时间跨度更令人印象深刻的是 Miško 将代码库减少到了 1500 行代码。尽管这个特定项目取得了成功,但谷歌还没准备好支持它。所以 AngularJS 被开源了。因此,其他开发者获得了试用它的机会,其中一个是 Marc Jacobs,具有些讽刺意味的是他在谷歌工作。谷歌收购了 DoubleClick 公司并开始重写该项目。Marc Jacobs 在该项目中担任技术负责人,并决定使用 AngularJS。最后,该项目也获得成功,从那时起,AngularJS 在谷歌内部被使用和维护。

AngularJS 在测试上投入大量精力的原因之一就有 Miško 在谷歌的项目的经验,我们在这里还没有涉及到。此外,AngularJS 使用依赖注入 (DI) 来实现控制反转 (IoC)。总之,AngularJS 是一个具有很强立场的框架,它提供了许多内置的工具。

更多的框架

2011 年,Ember.js 发布。它最初的名称是 SproutCore MVC 框架,后来改为 Ember。Ember 是一个基于 MVVM 模式的强约束框架。Apple Music、LinkedIn 和 Twitch 等公司也使用了 Ember。它的作者来自 Ruby on Rails 社区,他们更喜欢约定而不是配置,这是他们在 Ember 中采用的一种思维方式。这意味着 Ember 对项目的结构做出了很多假设,并从开发人员手中抽象出样板文件。

2012 年,Meteor.js 发布(2011 年首次命名为 Skybreak)。Meteor.js 是一个同构框架,它允许开发者同时为客户端和服务器编写 JavaScript。然而 Meteor 更像是一个平台,安装完后将会使用 Meteor CLI 来设置项目、运行应用程序、编译和压缩脚本和样式等。许多开发人员在 Meteor 初次发布时就对它感兴趣,但现在看来兴趣已经消退,至少从主流来看是这样。

未完待续...