Web开发人员应学习Angular的10个理由






4.86/5 (85投票s)
了解为什么 JavaScript 工具集 Angular 如此迅速地普及。
引言
毫无疑问,AngularJS——这个自诩为“超级英雄般的 JavaScript 框架”——正在获得越来越大的关注。在这篇文章中,我将经常将其简称为“Angular”。我很有幸在一家企业 Web 应用程序项目中使用 Angular 与一个大型团队(近 10 名开发者,很快将增至 20 多名)合作了半年多。更有趣的是,在我们转向使用强大的 TypeScript 和 Angular 组合之前,我们最初使用的是更传统的 MVC/SPA 方法,仅用纯 JavaScript 和 KnockoutJS 构建。需要注意的是,我们添加了使用 Jasmine 的全面测试,但总体而言,团队认为这种技术组合提高了我们的质量和效率:我们遇到的 bug 少了很多,交付功能的速度也快了很多。
如果你熟悉 Angular,这篇文章可能会给你一些以前未曾想过的新想法。如果你了解 Angular 并试图证明其在你公司或项目中的应用是合理的,这篇文章可以为你提供一些可能有帮助的背景信息。如果你完全不知道 Angular 是什么,请继续阅读,因为我将分享它为何如此强大,然后为你推荐一些可以帮助你快速上手相关资源的链接。
我只能猜测其他组织在采用 Angular 后也看到了积极的结果。根据 Google Trends 的数据,AngularJS(蓝色)与 KnockoutJS(红色)和“单页应用程序”(黄色)的流行度相比,正在爆炸式增长。
首届单轨 AngularJS 会议 ng-conf 在几分钟内就售罄了数百张门票。参加会议让我了解到,Angular 正在各种 Web 应用程序中得到应用,包括一些规模庞大的企业 Web 应用,例如 Google 自家的 Doubleclick Campaign Manager (DCM)。
这篇文章无意贬低 KnockoutJS、Ember、Backbone 或你可能已经在使用并熟悉的任何其他流行框架。相反,我想重点介绍我认为 Angular 迅速获得如此巨大势头的原因,以及任何从事 Web 应用程序开发的人都应该认真对待它,至少要多了解一下,以便决定它是否是适合你工具箱的工具。
1. Angular 提供数据绑定
(这为 XAML 开发者提供了一个去处。)我把这一项写得有点“玩笑”,因为大多数使用 Angular 的开发者可能从未接触过 XAML。没关系,XAML 在微软的 WPF、Silverlight 和现在的 Windows Store 应用开发中流行的原因值得一看,因为它们与 Angular 非常相似。如果你不熟悉 XAML,它是一种基于 XML 的声明性语言,用于实例化对象图并设置值。你可以定义各种具有属性的对象,并用标记声明一个将被创建的集合。最常见的对象类型是用户界面元素,如面板和控件,它们会创建显示。XAML 可以轻松布局复杂的 UI,并且可以随时间变化。XAML 支持继承(定义为父级子级的属性可以继承树中更高级别设置的值),并像 HTML DOM 一样冒泡事件。
XAML 的另一个有趣组件是数据绑定的支持。这允许在表示层和你的数据之间存在声明式关系,而不会在组件之间创建硬依赖。XAML 层理解存在一个契约——即“我期望发布一个名称”,而命令式代码只需公开一个属性,而无需了解其将如何呈现。这支持了无数的测试场景,将 UI 与底层逻辑解耦,从而使你的设计可以灵活变动而不必重构大量代码,并实现了设计者和开发者之间真正的并行工作流。
这听起来可能像空话,但我在许多项目中都亲眼见过。我记得有两个具体的例子。一个与微软合作的项目,我们必须在大约 4 个月内完成。我们估计需要 4 个月的手动开发,而一个独立的设计团队则需要大约 4 个月的设计时间才能全部完成——他们从线框图到效果图再到交互式模型和动态研究,以及其他让我庆幸的设计师可以做这些工作而我可以专注于代码的术语。当然,如果按照传统的顺序方法,我们将错过截止日期,需要等待 8 个月(4 个月的设计后接 4 个月的编码)。XAML 允许我们并行工作,通过就屏幕的接口达成一致——“我们将公开这些元素”。开发者负责获取数据以使这些属性可用,并围绕它们编写所有测试,而设计师则处理这些元素,进行修改、动画和移动,直到达到期望的设计。最终一切都完美地结合在一起。
另一个真实世界的例子是与一家有线电视公司合作的一个试点项目。我们正在构建他们交互式指南的 Silverlight 版本。唯一的问题是他们的 API 尚未准备好。我们能够基于一个映射用户体验的领域模型来设计系统——如列表、时间等——然后一旦 API 定义并可用,就用它们填充这些领域对象。同样,这实现了并行工作流,极大地提高了我们的效率和设计的灵活性。
我在 Angular 框架中看到了同样的原则。它实现了关注点分离,从而实现了标记本身 UI 和获取和处理数据的底层逻辑之间的真正的并行工作流。
2. Angular 摆脱了仪式和形式
你是否曾为模型创建一个你希望绑定到 UI 的文本属性?在各种框架中,这通常是如何完成的?在 Angular 中,这可以轻松完成,并且可以立即反映你在 `` 中输入的内容。
<input data-ng-model='synchronizeThis'/><span>{{synchronizeThis}}</span>
当然,你很少有构建如此简单应用的奢侈,但这说明了 Angular 世界中的数据绑定可以多么简单明了。要创建一个参与数据绑定的模型,几乎没有什么仪式或形式。你不必从现有对象派生,也不必显式声明你的属性和依赖项——在大多数情况下,你可以直接将已有的东西传递给 Angular,它就能正常工作。这非常强大。如果你好奇它是如何工作的,Angular 使用脏检查。
虽然我知道其他一些框架在这方面已经改进了,但从我们现有的需要将所有内容显式映射到一个中间对象来绑定数据的框架迁移到 Angular,就像一股清新的空气……事情开始更快地整合起来,我感觉自己重复的代码更少了(谁想定义一个联系人表,然后在服务器上定义一个联系人域对象,然后定义一个 JSON 对象,最后将其传递给一个客户端模型,只是为了,呃,显示联系人详情?)
3. Angular 提供依赖注入
依赖注入是 Angular 擅长的一项功能。我承认我曾怀疑客户方是否需要这样的东西,但我习惯了动态加载模块的关键场景。哦,等等——你说什么?没错,有了像 RequireJS 这样的库,你可以根据需要动态加载 JavaScript。然而,依赖注入真正 shines 的场景有两个:测试和单页应用程序 (SPA)。
对于测试,Angular 允许你将应用程序划分为逻辑模块,这些模块可以相互依赖但又能独立初始化。这使你可以采取非常战术性的测试方法,只引入你感兴趣的模块。然后,由于依赖项是注入的,你可以采用现有的服务,例如 Angular 的 `$http` 服务,并用 `$httpBackend` 模拟器将其替换用于测试。这实现了真正的单元测试,而无需依赖于服务启动或浏览器 UI 渲染,同时也能创建端到端测试。
单页应用程序使用动态加载来提供非常“原生应用程序”般的 Web 应用体验。人们喜欢大声说 SPA 的缩写,好像这是什么新鲜事,但我们从 Atlas 和 Ajax 的时代就开始构建这种风格的应用了。讽刺的是,如今 Ajax 驱动着 SPA,尽管 XML 很少再被使用,因为现在一切都是 JSON。你会发现这些应用程序可以快速增长,对各种服务和模块有大量依赖。Angular 使组织这些模块并按需获取它们变得容易,而无需担心诸如“它属于哪个命名空间?”或“我是否已经启动了一个实例?”之类的问题。相反,你只需告诉 Angular 你需要什么,Angular 就会为你获取并管理对象的生命周期(例如,你不会到处都是同一简单服务获取联系人信息的 100 个副本)。
接线依赖服务的 DI 示例
var $$jsInject = new WintellectJs.$$jsInject();
$$jsInject.register("car", [Car]); // annotating deps at registration
$$jsInject.register("tires", ["engine", Tires]);
$$jsInject.register("engine", ["gas", Engine]);
$$jsInject.register("gas", [Gas]);
Car.$$deps = ["engine", "tires"]; // annotating deps on class
var car = $$jsInject.get("car");
console.log(car.drive());
4. Angular 允许开发者声明式地表达 UI 以减少副作用
声明式 UI 有很多优点。我在前面讨论 XAML 时已经提到了一些,HTML 也是如此。拥有结构化的 UI 更容易理解和操作。不一定是程序员的设计师更容易学习标记语言,而不是编程语言。使用 jQuery,你需要了解很多关于文档结构的信息。这会带来两个问题:首先,结果是大量的“胶水”代码,这些代码与 UI 的更改紧密耦合,很不稳定;其次,你会得到很多“魔法”,因为从标记上看,不清楚 UI 会做什么。换句话说,你可能有许多“在后台”连接的行为和动画,从 `form` 标签中看不出有任何验证或过渡正在发生。
通过声明你的 UI 并将标记直接放入 HTML,你可以将表示逻辑保存在一个地方,并与命令式逻辑分离。一旦你理解了 Angular 提供的扩展标记,上面的代码片段就会清楚地显示数据在哪里被绑定以及绑定到什么。像指令和过滤器这样的工具的添加,使得 UI 的意图更加清晰,同时也使得信息是如何被塑造的更加清晰,因为塑造是在标记本身中完成的,而不是在某个隔离的代码中。
维护大型系统——无论是大型软件项目还是拥有大型团队的中型项目——都在于减少副作用。副作用是指当你改变某些东西时,会产生意想不到甚至灾难性的结果。如果你的 jQuery 依赖于一个 ID 来定位一个元素,而设计师更改了它,你就会丢失绑定。如果你显式地填充下拉列表的选项,而设计师(或客户,或你自己)决定切换到第三方组件,代码就会中断。声明式 UI 通过在源头声明绑定,消除了粘合行为到 UI 的隐藏代码的需要,并允许数据绑定将对想法(即“一个列表”)的依赖与想法的表示(即,下拉列表 vs. 项目符号列表)解耦,从而减少了这些副作用。
5. Angular 支持“DD……呃,测试
无论你支持测试驱动开发 (TDD)、行为驱动开发 (BDD),还是任何驱动开发方法论,Angular 都支持这种构建应用程序的方法。我不想在这篇文章中详细介绍测试的所有优点和原因(事实上,我惊讶于在 2013 年,人们仍在质疑其价值),但我最近更倾向于传统的“先测试”方法,并且它确实有帮助。我相信,在我们的项目中,Jasmine 的引入以及我们包含的测试将缺陷减少了高达 4 倍。可能少一些(也可能多一些),但确实有显著的下降。这不仅仅是因为 Angular——这是需求、良好的验收标准、理解如何正确编写测试以及拥有运行测试的框架的结合——但构建这些测试肯定更容易。
这是一个展示 Angular 视图模型或“作用域”继承的示例测试
it("overrides from the parent", inject(function ($rootScope) {
var scope = $rootScope.$new();
var childScope = scope.$new();
scope.a = 1;
scope.b = 2;
childScope.b = 3;
expect(scope.$eval('a+b')).toBe(3);
expect(childScope.$eval('a+b')).toBe(4);
})); <span style="font-size: 9pt;"> </span>
如果你想看看这是什么样子,可以看看我的 6502 模拟器,然后浏览 源代码。除了初始的管道设置,该应用程序是用纯粹的先测试方法编写的。这意味着当我想要添加一个操作码时,我先为操作码编写测试,然后实现它。当我想要扩展编译器时,我为编译的期望结果编写一个失败的测试,然后重构编译器以确保测试通过。这种方法为我节省了时间,并改变了我构建和思考应用程序的方式,也为它提供了文档——你可以自己查看规范来理解代码应该做什么。在 Angular 中模拟依赖项并注入它们的能力非常重要,正如你从示例中可以看到的,你可以测试从 UI 行为到业务逻辑的一切。
6. Angular 支持大规模并行开发
我们在项目初期遇到的最大问题之一是开发者之间互相干扰。这部分是纪律问题,即使是原始的 JavaScript,你也可以遵循使代码更模块化的模式,但 Angular 将其提升到了另一个层次。这并不是说它完全消除了依赖性,但它肯定使它们更容易管理。举个具体的例子,应用程序中有一个大型网格,用于驱动几个关键操作。在传统的 JavaScript 应用程序中,将这个网格扩展到大型团队可能会是一场合并噩梦。然而,在 Angular 中,将各种操作分解成自己的服务和子控制器是很容易的,开发者可以独立测试和编码,而不会经常发生冲突。
显然,对于大型项目来说,这是至关重要的。它不仅仅是技术本身如何支持客户端功能,更是如何支持一种工作流程和流程,使你的公司能够扩展团队。
7. Angular 支持设计师 <-> 开发者工作流
好吧,我是在开玩笑,对吧?你可以通过 HTML、CSS 和其他有趣的 기술 来实现这一点。之所以将其列为一个单独的要点,是因为它与 Angular 的契合度如此之高。设计师可以添加标记,而不会完全破坏应用程序,因为它依赖于特定的 ID 或结构来定位元素并执行任务。相反,重新排列代码部分就像移动元素一样容易,相应的绑定和过滤代码也随之移动。虽然我还没有见过一个精通此道的环境,开发者与 UI/UX 团队共享“设计契约”,但我相信这为时不远——基本上,团队同意将显示哪些元素,然后设计团队可以按照自己的意愿布局,而开发团队则将 `$scope` 与控制器和其他逻辑连接起来,最后两者完美结合。我们当初使用 XAML 就是这样做的,现在没有任何东西可以阻止你用 Angular 做同样的事情。
如果你是微软开发者,并且使用过 Blend……看到一个理解 Angular 并能提供 UI 来设置绑定和设计时数据的 IDE,是不是很酷?这种能力是存在的,只是需要被构建出来,并且根据我看到的人气,我相信这不会花太长时间。
8. Angular 提供控件模板(“指令”)
我听到的关于迁移到 MVC 的最常见抱怨之一是“我们那些控件怎么办?”早期的看法是,控件在非 ASP.NET 空间中不起作用/不会起作用,但使用其他平台的 Web 开发者知道情况并非如此。在 Web 上有很多方法可以采用可重用代码,从 jQuery 插件的概念到第三方控件供应商,比如我最喜欢的 KendoUI 之一。
Angular 支持一种称为“指令”的新场景,它允许你创建新的 HTML 元素和属性。在之前的 T6502 示例中,“`data-ng-model`”指令允许数据绑定发生。在我的模拟器中,我使用了一个指令来创建两个新标签:“`console`”标签,用于写入控制台消息,以及一个“`display`”标签,它使用 SVG 来渲染模拟器的像素(好吧,到这个时候如果你已经查看过了,我意识到这更像是一个模拟器)。这为开发者提供了他们的控件——更重要的是,他们对控件的控制。
我参与的一个项目已经演变成了几十个指令,它们都参与了之前的几点内容
- 指令是可测试的
- 指令可以并行处理
- 指令提供了一种声明式的方式来扩展 UI,而不是使用代码来连接新的构造
- 指令减少了仪式和形式
- 指令参与依赖注入
还记得我提到的那个作为项目核心的巨大网格吗?我们(以及几乎所有写过的企业 Web 应用程序)都使用了很多网格。我们使用的是 KendoUI 版本,初始化网格需要几个步骤。对于我们的目的来说,许多配置选项在网格之间是一致的,那么为什么要让开发者输入所有代码呢?相反,我们允许他们放置一个新元素(指令),用几个属性(指令)标记它,然后就可以运行了。
9. Angular 有助于管理状态
我犹豫是否要添加这一点,因为精通的、经验丰富的 Web 开发者都理解 HTTP 的概念以及如何管理他们的应用程序状态。正是 ASP.NET 灌输的“状态幻觉”在开发者转向 MVC 时造成了困惑。我曾经在一个相当受欢迎的论坛上读到一位自称的架构师宣称 MVC 是一种低劣的 Web 设计方法,因为他不得不“构建自己的状态管理”。什么?这只是表明了对 Web 工作原理的完全不理解。如果你依赖于 15K 的视图状态来运行你的应用程序,那你就做错了。
我指的是更多地是客户端状态,以及你如何在浏览器中管理你的应用程序的属性、权限和其他常见的横切关注点。Angular 不仅处理依赖注入,还为你管理组件的生命周期。这意味着你可以用一种非常不同的方式来处理代码。这里有一个简单的例子来解释我的意思。
应用程序的一部分涉及复杂的搜索。这是一个传统的模式:输入搜索条件,点击“搜索”并看到结果网格,然后点击一行查看详细信息。最初的实现涉及两个页面:首先是详细的条件页面,然后是带有从右侧滑入的窗格来显示当前选定行的详细信息的网格页面。后来在项目中,这被重构为一个搜索条件的对话框,该对话框会覆盖网格本身,然后是一个单独的全屏页面用于显示详细信息。
在传统的 Web 应用程序中,这会涉及重写一些逻辑。我可能会有一些调用来获取详细信息,并期望将它们传递到同一页面上的一个面板中进行详细显示,然后突然需要重构它,将详细 ID 传递到一个单独的页面,并让该页面进行调用,等等。如果你在 Web 开发方面有一定经验,你一定经历过一些重写,感觉为了仅仅移动东西而付出了太多。需要管理多个“状态”部分,包括选择条件和正在显示的确切记录的标识符。
在 Angular 中,这就像一次轻松的重构。我为搜索对话框创建了一个控制器,为网格创建了一个控制器,为详细信息页面创建了一个控制器。一个父控制器负责跟踪搜索条件和当前详细信息。这意味着从一种方法切换到另一种方法实际上只需要重新组织我的标记。我将详细信息移到一个新页面,将条件切换到一个对话框,我唯一真正需要编写的代码是当被请求时调用对话框的新函数。所有其他逻辑——获取和过滤数据并显示它——都保持不变。这是一次快速的重构。这是因为我的控制器不关心页面的组织方式或流程——它们只是专注于获取信息并通过作用域暴露它。组织是路由的关注点,我们使用 Angular 的路由机制来“重新路由”到新方法,同时保留了 UI 背后的相同控制器和逻辑。即使是搜索条件的标记也保持不变——它只是从一个用作完整页面的模板,变成了在一个对话框中使用的模板。
当然,这种重构是可能的,因为该应用程序是一个混合型单页应用程序 (SPA)。
10. Angular 支持单页应用
如果你错过了,这一点是承接上一点的。单页应用程序越来越受欢迎是有原因的。它们填补了一个非常具体的需求。越来越多的功能被转移到 Web 上,浏览器最终认识到其作为分布式计算节点的潜力。SPA 应用程序在设计上响应速度更快(尽管其中一部分是感知问题)。它们可以提供一种几乎像 Web 上的原生应用程序一样的体验。通过在客户端渲染,它们可以减少服务器负载并减少网络流量——你发送的是数据负载,而不是完整的页面标记,然后在客户端将其转化为标记。当然,Angular 并不会强迫你构建一个 SPA 应用,它只是提供了大量的内置支持。
根据我的经验,大型应用程序构建为混合 SPA 应用更有意义。混合的意思是,不是将整个应用程序视为一个单页应用程序,而是将其划分为逻辑工作单元或系统中的“活动”,然后将每个活动实现为一个 SPA。最终会有一些区域导致完整的页面刷新,但关键的交互发生在一系列不同的 SPA 模块中。例如,联系人可能是一个“迷你”SPA 应用,而产品是另一个。Angular 提供了所有必要的基础设施,从路由(能够将 URL 映射到动态加载的页面)、模板,到日志记录(深度链接并允许用户使用内置的浏览器控件进行导航,即使页面没有刷新),来搭建功能性的 SPA 应用程序,并且非常擅长让你在各个区域或“迷你 SPA”部分共享所有组件,从而为用户提供一个单一应用程序的体验。
结论
我经常听到 Angular 被称为一个框架。虽然它肯定可以被用作框架,但我认为它更像是一个库和工具集。它提供了与竞争对手库相同的功能,如数据绑定,还增加了依赖注入、输出过滤以及通过指令进行的先进模板化。Angular 真正出彩的地方在于它允许你构建自己的模块化、可重用的 JavaScript,然后将其插入到库中与其他组件进行交互。在最近的一次采访中,我将 Angular 描述为一种面料,你可以在其上编织应用程序的组件来提供最终的体验。这一点很重要,因为它不会让你完全被锁定在框架中,而且正如我的团队所经历的那样,它简化了从现有应用程序迁移到使用 Angular 的应用程序的过程。