JavaScript 框架历史(上)

JavaScript 框架历史(上)

本文是人工翻译的国外文章,原文链接:History of JavaScript Frameworks (Nicklas Envall) ,限于译者水平,难免出现错误,请多多包涵。

原文内容较长,因此拆分为几部分进行翻译发布,本文是第一部分。

前言

JavaScript 生态中有着丰富的框架和库。有人说我们处在 JavaScript 的复兴中,这是一个创新和快速迭代的时代,一个旧工具不断被新工具取代的时代,一个不断突破 JavaScript 应用程序极限的时代。然而,也有许多人厌倦了 JavaScript 巨大的生态。在 GitHub 的某个角落可能还存在着一个你从没有听过的、收藏过万的框架。

但是,为什么有这么多框架,它们的意义何在?为了了解它们存在的意义,我们将回望过去。本文将带你来一场从上世纪九十年代到现在的时间旅行,旅途中我们会遇到各式各样的框架和库,并从中了解到它们的模式、故事、案例。

JavaScript 框架的诞生

JavaScript 诞生于 1995 年,有了让页面生动丰富的可能。有了 JavaScript,我们可以响应用户事件、执行动画效果、验证输入等等,我们使用术语 dynamic HTML (DHTML) 来描述这些概念。但是九十年代,由于平台之间不兼容,JavaScript 代码很难同时在多个平台上正常运行,DHTML 也就没有用武之地。平台的灾难源于 浏览器大战。 网景(Netscape)公司和微软(Microsoft)公司为了打败对方,就通过给浏览器增加各种新特性吸引开发者,而这就导致了平台不兼容。结果就是,开发者很难保证他们的代码能在用户的浏览器上正常运行。

幸运的是,1997 年,网景和微软共建了一个 JavaScript 语言规范,并提交到了 ECMA 国际(ECMA International) 。ECMA 国际是一个标准组织。ECMA 最终发布了 ECMAScript 。所以从 ECMAScript 成为语言的标准规范后,就有了这么一个说法------ JavaScript 就是 ECMAScript。新的特性不断的被提出并发布新的 ES (ECMAScript 缩写) 版本。这就是为什么你总能看到一些关于 ES3、ES5、ES6 等等的文章。由于 JavaScript 引擎通常由浏览器自己开发实现,所以是否遵循规范也取决于他们自己。

浏览器大战最终也结束了。微软通过在 Windows 操作系统免费预装 Internet Explorer 4 无情地打败了网景,成为了最终的赢家。胜利的结果就是 Internet Explorer 占领了市场多年。2001 到 2006 年,Internet Explorer 只发布了一个新版本,革命停止了。不过局面总算稳定了,越来越多的开发者开始尝试 DHTML。但是还存在一些困难,开发社区还是很难接受 JavaScript,JavaScript 一直被认为是只有在不得不增加 UI 组件或在客户侧校验的时候才会使用的低等的编程语言。然而,经过多年的发展,JavaScript 已经发生演化并在 Web 开发领域有广泛的应用。到今天,即便 JavaScript 获得过一些嘉奖,但还是有很多人看不起。这种对 JavaScript 的厌恶,有时是有些道理的,而有时却是缺乏理解。

当公司开始尝试新的网络技术,标志着新时代的曙光开始出现。从九十年代初以来,网站就被分为一系列的网页。每当用户请求查看一个网页,就需要浏览器加载整个页面。因此,网站的浏览体验还不如使用桌面应用程序。这个问题不仅在导航过程中,也存在于向服务器发送数据的时候。比如,当用户发送一个填好的表单到服务器时,将不得不等待确认页面。这种糟糕的体验促使开发者试验不同的技术,来让网站的表现更像桌面应用程序。说来也怪,微软早就发明了解决这种问题的方法。微软的 Outlook Web Access 团队发明了通过客户端脚本发送 HTTP 请求的技术,这样客户端可以向服务器请求数据,而不用重新加载整个页面,请求到的数据能动态更新部分页面。这样,由于请求是在后台处理的,用户在向服务器请求的同时仍然可以进行页面交互。随后,这项技术就加到了 Internet Explorer 5.0 中。后来就成为了 XMLHttpRequest, 可以通过 JavaScript 来进行请求。中间花了足有六年,这项技术才变得广为人知。

2005 年,出现了一些令人印象深刻的网站如 Gmail、Google Maps、Flickr,这也引起了一名用户界面设计师 Jesse James Garrett 的注意,同时 JavaScript 也受到了大量关注。这些网站是使用了 XMLHttpRequests 及一系列技术创建的类桌面应用程序。Garrett 为这一系列技术创造了一个术语------ Asynchronous Javascript + XML (AJAX)。AJAX 能让用户可以不必重载页面就能向服务器请求数据。因此,开发者就可以利用 DHTML 技术用从服务器获取到的数据更新页面展示。虽然这些技术已经存在有一段时间了,但仍引起了广泛的觉醒,并促使开发者进一步地发掘网络应用的极限。

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

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

这些框架解决的问题有:

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

2000-2005 年间,出现过许多的框架,而到现在,很多已经消失了,但仍有一个框架是跨越时代的存在,你或许听说过,就是 jQuery

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

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

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

MV* 框架的崛起

2010 年,Knockout.js、Backbone.js 与 AngularJS 出现了,它们都属于 MV* 系列

我们现在将学习 MV* 系列,继续我们框架之旅。

什么是 MV* ?

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

graph LR V["View(视图)"]--交互-->C C["Controller(控制器)"]--读/写-->M M["Model(模型)"]--更新-->V

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

graph LR V["View(视图)"]--交互-->P P--更新-->V P["Presenter(呈现层)"]--读/写-->M M["Model(模型)"]--事件-->P

Model--view--viewmodel (MVVM),视图层与视图模型层自动进行绑定。因此,用户交互就通过数据绑定转发到视图模型层。然后,模型层依然是包含业务逻辑或数据。

graph LR V["View(视图)"]<--双向绑定-->VM VM["ViewModel(视图模型)"]--读/写-->M M["Model(模型)"]--事件-->VM

最后,我们需要理解的框架与库之间的关键差异。Backbone.js 和 Knockout.js 是库,而 AngularJS 是框架。也就是说,AngularJS 对你的应用架构有很强的约束,而这方面 Backbone.js 和 Knockout.js 会给你更多的自由,由你自己决定。

MV* 框架的历史

Knockout.js(缩写为 KO)发布于 2010 年 7 月,作者是 Steve Sanderson,他也是 Blazor 第一版的作者。Knockout 的基础是 MVVM 设计模式,由 observablesbindings 实现。

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

html 复制代码
<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,他期望 Knockout 和 jQuery 能在一块使用,他的目的是避免复杂的事件处理,并给开发者一个可以绑定用户界面的数据模型。另外,库还包含了几个工具函数,像数组过滤、JSON 解析。总之,这是一个允许我们使用双向绑定并保持我们自由设计项目架构的库。

2010 年 10 月,CoffeeScript 的作者 Jeremy Ashkenas 发布了 Backbone.js。Backbone.js 构建应用的方法和 MVP 模式相似。Backbone.Model 实现了 MVP 模式中的 M,templates (像 Underscore/Mustache)实现 V,Backbone.View 实现 P。

不像其他框架或库使用声明式方式,Backbone.js 使用指令式方式更新 DOM。举例,看下面 Backbone.View 中的 render 方法。它包含了一个选择器,能够声明式的更新 DOM(尽管 template 是一种颇有争议的声明方式)。另一方,Knockout 仅关注在模型上,而 Backbone 需要我们手动更新 DOM 。

html 复制代码
<div id="price-container"></div>

<!-- VIEW -->
<script type="text/template" id="price-template">
  <p>The price is <%- price %></p>
  <button id="priceBtn">Increase the price</button>
</script>

<script>
  // MODEL
  const PriceModel = Backbone.Model.extend({
    defaults: {
      price: 10,
    },
  });

  // PRESENTER
  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 on create
    },

    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();

  // Start everything by creating the view
  new PriceView({ model: priceModel });
</script>

Backbone 旨在提供构建 JavaScript 应用程序所需,核心是一种通过利用模型和视图来处理数据和 UI 的方法。Backbone 提供了比原生 JavaScript 更好用的操作方法。此外,它还可以通过 Backbone.Router 将应用中的定位映射到 URL,从而实现客户端路由。总之,Backbone 库提供了一系列工具来构建小型或大型应用程序。

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

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

html 复制代码
<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">
  <!-- one-way binding -->
  <p>{{msg}}</p>
  <p>The price is {{price}}</p>
  <!-- Event binding -->
  <button ng-click="increasePrice()">Increase the price</button>
  <!-- two-way data binding -->
  <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 是一个具有很强立场的框架,它提供了许多内置的工具。

其他框架

Ember.js 发布于 2011 年。它最初的名字是 SproutCore MVC 框架 ,但后来改为 Ember。Ember 是一个基于 Model-View-ViewModel (MVVM) 模式的强约束框架。它的用户有苹果音乐、领英和 Twitch 等公司。作者来自 Ruby on Rails 社区,他们更喜欢「约定优于配置」这种思维方式。这意味着 Ember 对项目架构做了很多假设,并且从开发人员那里总结抽象了最佳实践。

Meteor.js,发布于 2012 年(2011 年最初命名为 Skybreak)。Meteror.Js 是一个 同构框架 ,它允许开发者同时为客户端和服务器编写 JavaScript 代码。然而 Meteor 更像是一个平台,安装之后,你可以使用 Meteor CLI 来设置项目、运行应用、编译和缩小化脚本和样式等等。许多开发者在 Meteor 第一次发布的时候对它很感兴趣,但是现在看来这种兴趣已经消退了,至少从主流上来说是这样。

相关推荐
理想不理想v5 分钟前
vue经典前端面试题
前端·javascript·vue.js
不收藏找不到我6 分钟前
浏览器交互事件汇总
前端·交互
小阮的学习笔记19 分钟前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
YBN娜20 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=20 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck25 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。1 小时前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼1 小时前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript