迈向Angular2:基于TypeScript的高性能SPA框架(txt+pdf+epub+mobi电子书下载)


发布时间:2020-09-15 18:19:43

点击下载

作者:明科·基彻

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

迈向Angular2:基于TypeScript的高性能SPA框架

迈向Angular2:基于TypeScript的高性能SPA框架试读:

内容简介

本书由Angular之父Miško Hevery作序,作者为Angular开发团队成员之一。基于对Angular 2架构和设计方面的深入理解,本书选材精准、内容实用。全书从一个小例子开始引导读者快速上手,详细介绍Angular 2带来的所有新特性,包括组件、指令、TypeScript、新的路由机制、管道、服务端渲染等。

本书读者对象包括Angular 1.x的开发者、想直接从Angular 2开始入手的学习者,以及前端技术架构师等。

译者序

谢谢我的粉丝们。

Angular 2的发布标志着这门框架已经走向成熟和稳定,正如官方申明所说:Angular 2的核心架构已经定型,不会再做颠覆性的设计。

然而,鉴于你们前端界“每隔6个月就重写一遍”的现状,我本人对以上申明持观望的态度。纵观当前市场上的所有前端框架,只有jQuery和ExtJS大体上做到了“核心架构不变”。

但是与其他所有前端框架相比,Angular 2有一点非常具有前瞻性,它是基于TypeScript开发的。JavaScript之父Brendan Eich对他自己发明的语言有一段经典的评价:

与其说我爱Javascript,不如说我恨它。它是C语言和Self语言一夜情的产物。18世纪英国文学家约翰逊博士说得好:它的优秀之处并非原创,它的原创之处并不优秀。

对于这段评价,只要写过JavaScript的人,一定会表示不能同意更多。JavaScript的灵活性、动态性,以及目前各种新标准的剧烈变革直接把学习成本推高了N个数量级,对于大规模的开发团队尤其如此。因此,Angular 2直接基于静态类型的TypeScript来进行开发,无疑是合适的,直接从语言层面上就把一些坑给填了,这是非常具有战略眼光的决策。对于有Java、C++,或者ActionScript背景的开发者来说,TypeScript的学习成本几乎为零,你只要花30分钟浏览一下大致的语言特性就能写得风生水起了。

在当前这个时间节点上,各种前端技术层出不穷,让人有目不暇接之感。从学习的角度而言,这是一种负担,但是这种状况同时也意味着大量的工作机会,毕竟沧海横流方显英雄本色。

本书所有代码都在GitHub上,原作者会不断更新:

https://github.com/mgechev/switching-to-angular2大漠穷秋2016-06-06

推荐序

Angular 2依然是Angular,只是更强大而已。它依然构建在那些相同的原则之上,这些原则也是你们热爱AngularJS的原因:构建单页应用的一种快速而强大的解决方案。在Angular 2中,应用将会运行得更加快速,对SEO和移动设备更加友好,并且是天然跨平台的。虽然Angular 2已经对AngularJS中的很多概念做出了大幅度的演进,但是仍然保留了上一个版本的设计哲学。《迈向Angular 2:基于TypeScript的高性能SPA框架》一书完全可以佐证以上观点。所以,Minko的这本书将会成功地帮你把思维模式从AngularJS1.x切换到Angular 2。从你与Angular 2的初次邂逅直到最后,Angular的核心概念将会贯穿始终。这本指南将会帮你切换到用Angular做事的新方式上去。Minko将会引导你贯通所有变更的内容,包括引入的所有新特性:组件、指令、TypeScript、新的路由机制,以及开始使用Angular 2开发你的下一个项目时所需要的一切内容。

如今的Web开发领域日新月异,Angular 2接受了这一挑战,并且构建在AngularJS的遗产之上。因此,对Angular社区来说,出现高质量的学习资料是极其重要的,而Minko的这本书将会帮助Angular开发者迈出面向未来的第一步。Miško HeveryAngularJS与Angular 2之父

关于作者

Minko Gechev是一名软件工程师,他坚定地信仰开源软件。他开发过许多项目,包括Angular JS 1. x 和Angular 2 style guides、Angular 2-seed、Angular 2项目静态代码分析器、aspect.js、angular-aop,以及很多其他项目。他开办了JavaScript、Angular等web技术培训课程。

Minko热衷于进行各种计算机科学概念实验并把它们投入到实战中去。他在ng-vegas、AngularConnect、ITWeekend Kiev、Angular JS-SF及Angular Berlin等全球论坛上进行过关于Angular与软件开发相关话题的演讲。

感谢Misko Hevery在软件工程和技术审查方面对本书做出的巨大贡献。他竭尽全力为我提供了精确的内容。为了让书中的实例代码运行起来更简单,我开发了Angular 2-seed这个项目(这个项目提交在github上——译者注)。Ludovic Hénin是该项目的核心贡献者,他的工作让这个项目远远超越了Angular 2入门级的水平。我还要感谢Daniel Lamb、Radoslav Kirov 和Tero Parviainen,他们给我提供了极其宝贵的反馈意见!

没有Packt出版团队的全心工作,我不可能完成此书。

最后,我想感谢谷歌的Angular团队,感谢他们为大家带来这样一个框架,也为我带来源源不断的灵感。

关于审校者

Miško Hevery是AngularJS框架之父。他酷爱化繁为简。他目前就职于Google,此前他在Adobe、Sun Microsystems、Intel以及Xerox工作过,这些经历让他成为了使用Java、JavaScript、Flex以及ActionScript这些web技术开发web应用方面的专家。

Daniel Lamb是一位资深的软件开发专家,同时也是一位乐于分享知识的作家,他专攻大规模架构和web前端开发。在16年职业生涯中,他支持了数十亿计的访问,让数亿人能够连接到一起互相交流。

序言

AngularJS是一个JavaScript开发框架,致力于让web应用开发变得更加简单。目前,它已经被应用在大规模、大流量的网站中,这些网站饱受性能低下、移植性差的困扰,同时还面临SEO不友好、复杂度大的问题。Angular 2改变了这一切。

它是一款非常现代的框架,可以利用它构建性能更高、健壮性更强的web应用。《迈向Angular 2:基于TypeScript的高性能SPA框架》是掌握Angular 2最快捷的方式,它将引领你进入Angular 2的全新世界。

读完本书,你将会具备利用Angular 2所提供的一系列新特性来快速有效地构建应用的能力。

本书内容

第1章 Angular 2快速上手:开启Angular 2新世界的旅程。这一章描述了框架设计决策背后的一些主要因素。我们将会看到形成这门框架的两种主要驱动力—web的当前状态以及前端框架的进化。

第2章 Augular 2应用的基础构件:简要介绍Angular 2引入的一些核心概念。我们将会探讨AngularJS 1.x所提供的基础构件与最近一个主版本之间的差异。

第3章 TypeScript速成:Angular 2是语言无关的,但是Google推荐大家利用TypeScript所带来的静态类型特性。在这一章中,你将会学习利用TypeScript开发Angular 2应用的所有必要语法!

第4章 Angular 2组件和指令入门:解释开发用户界面所需要的核心构件—Directive以及Component。我们将会深度解析视图封装、内容投影、输入输出、脏值检测等概念。同时还会讨论一些高级主题,例如:模板引用,以及使用不可变数据类型加快应用的运行速度。

第5章 Angular 2中的依赖注入:这一章将会全面解析这门框架中最强大的特性之一:依赖注入机制。它最初是由AngularJS 1.x引入的。我们可以利用这一特性来编写更加便于维护、测试,并且更易于理解的代码。在这一章结束的时候,我们将会理解如何在Service中定义业务逻辑,然后利用DI(依赖注入)机制把它和UI黏合到一起。我们还会解析一些更加高级的主题,例如:注射器的层级结构、配置provider等。

第6章 Angular 2中的路由和表单:这一章将会探索在开发实际应用的过程中如何使用用来维护form的新模块。我们将会实现一个页面,可以显示form中输入的值。最后,我们会利用基于组件的路由把各个单独的页面黏合成一个完整的应用。

第7章 详解管道以及与RESTful服务端之间的通信:深度解析路由和表单模块。这一章将会解析如何开发数据模型驱动型表单,以及如何定义参数化路由、子路由。我们还会解释HTTP模块,以及如何开发有状态和无状态管道。

第8章 开发体验与服务端渲染:探索开发Angular 2应用中的一些高级主题,例如:在WebWorker中运行应用,以及服务端渲染。在这一章中的第二部分,我们将会学习一些全新的开发工具,从而让日常开发工作更加轻松一些,例如:angular-cli、angular2-seed,以及解释热重载的概念等。

阅读准备

对于本书中的绝大多数实例,你需要:一个简单的文本编辑器或者IDE、装好Node.js和TypeScript、能上网,以及一个浏览器。

对于每一章所提供的实例代码,对应章节都会解释需要安装的软件。

目标读者

你想深入学习Angular 2吗?或许你想先评估一下最新的变更再决定是否跟进?如果是,那么《迈向Angular 2:基于TypeScript的高性能SPA框架》就是为你量身定制的。

为了能够充分理解本书内容,需要对AngularJS 1.x有基本的理解,同时需要对JavaScript非常熟悉。阅读本书不需要预先了解 Angular 2所引入的变更。

排版约定

在本书中,根据不同的内容会采用各种不同的文字风格。下面解释其中一些文字风格的含义。

文本中的代码单词、数据库表名、目录名、文件名、文件后缀、路径名、伪URL链接、用户输入,以及Twitter路径将会这样排版:“你将会看到相同的结果,但是test.js这个文件不会存储到硬盘上。”

代 码块将会这样显示: @Injectable() class Socket { constructor(private buffer: Buffer) {} } let injector = Injector.resolveAndCreate([ provide(BUFFER_SIZE, { useValue: 42 }), Buffer, Socket ]); injector.get(Socket);

当需要注意代码块中的某个部分时,对应的行或者项目将会加粗显示: let injector = Injector.resolveAndCreate([ provide(BUFFER_SIZE, { useValue: 42 }), Buffer, Socket ]);

对于代码库中的代码,本书对应的实例代码之前都会有一行注释,用来说明代码对应的文件路径,所有路径都相对于app目录:// ch5/ts/injector-basics/forward-ref.ts @Injectable()class Socket { constructor(private buffer: Buffer) {…}}

新术语和重要的词语都会加粗显示。你可以在屏幕上看到的单词,例如菜单和对话框中的文本,将会这样显示:“当标签渲染到屏幕上之后,用户唯一能看到的内容就是这样一段文本:Loading…”。警告或者重要的内容将会出现在这样的方框里面。小提示和小技巧将会这样显示。

代码下载

可以从GitHub下载本书中的所有实例代码:https://github.com/mgechev/switching-to-angular2.

下载步骤如下:

1. 在浏览器地址栏中输入以上URL路径。

2. 单击屏幕右侧中间的“Download ZIP”按钮开始下载。

也可从http://www.broadview.com.cn下载所有已购买的博文视点书籍的示例代码文件。

勘误提交

虽然我们已经尽力谨慎地确保内容的准确性,但错误仍然存在。如果你发现了书中的错误,包括正文和代码中的错误,请告诉我们,我们会非常感激。这样,你不仅帮助了其他读者,也帮助我们改进后续的出版。如发现任何勘误,可以在博文视点网站相应图书的页面提交勘误信息。一旦你找到的错误被证实,你提交的信息就会被接受,我们的网站也会发布这些勘误信息。可以随时浏览图书页面,查看已发布的勘误信息。第1章Angular 2快速上手

2014年9月18日,Angular 2 代码库迎来了首次公开commit。几周后,在ng-europe会议上,来自核心团队的Igor和Tobias对于理想中的Angular 2做了一个简要的声明。当时的版本与最终目标相距甚远,然而有一点是肯定的:这将是一个与AngularJS 1.x.完全不同的新框架。

这一声明引发了诸多疑惑和争论。进行剧烈变更的原因显而易见:AngularJS 1.x 已经无法充分发挥最新web技术的威力,而且无法完全满足开发大规模 JavaScript 应用的需求,而一个全新的框架可以让Angular开发者以更简单、更直接的方式来开发web应用。然而,大家普遍感到担忧。如果把所使用的第三方软件代码库升级到最新版本,然后导致不能向下兼容的问题,这是开发者最大的噩梦之一。在Angular这个案例上,自从那次声明之后,迁移的问题看起来无比艰巨,甚至毫无可能。后来,在2015年的ng-conf和ng-vegas会议上,大家提出了各种各样的迁移思路。Angular社区的成员聚在一起分享了很多想法,大家期待在保留AngularJS 1.x一些精华的基础上,能享受到Angular 2的各种优点。

本书是此项目的一部分。升级到Angular 2不是小打小闹的事情,但升级是值得的。在Angular 2剧烈变更以及缺乏向下兼容性的背后,主要的推动力是web技术的演进以及来自于AngularJS 1.x的经验。《迈向Angular 2:基于TypeScript的高性能SPA框架》这本书将会帮助你学习这门新的框架,告诉读者这门框架是如何一步一步发展到今天的;以及对于现代web来说,在构建高性能、可扩展的单页应用方面,为什么Angular 2带来的新特性具有显而易见的优势。Web的进化——新框架时代

近年来,web已经发生了大幅度的进化。在实现ECMAScript 5的同时,ECMAScript 6也开启了它的发展历程(即现在众所周知的ECMAScript 2015,也叫ES2015)。ES2015对这门语言做了大幅度修改,例如:对模块化、块级作用域增加了语言级的内置支持;同时增加了很多语法糖,例如:支持class以及解构赋值。

与此同时,Web Component的概念被发明出来。Web Component允许我们自定义HTML标签并在上面绑定行为。在现有的HTML标签基础上扩展新标签(例如对话框、图表、数据表格等)是很难的,主要原因是对这些新标签的API进行巩固和标准化需要很长时间。更好的解决方案是允许开发者按照自己的想法去扩展现有的标签。Web Component带来了很多优势,例如更好的封装性、提供的标签具有更好的语义、更好的模块化,并且能让开发人员和设计师之间的交流更加顺畅。

我们知道,JavaScript 是一门单线程语言。最初开发这门语言的时候,目标只是用来编写简单的客户端脚本,但是随着时间的推移,它的角色发生了很大的转变。现在,我们可以利用HTML5提供的API来处理音频和视频文件,用全双工通道与外部服务进行通信,传输和处理大块原始数据,如此等等。如果所有这些耗时运算都在主线程里面执行,用户体验会非常糟糕。因为在执行耗时运算的时候,用户界面会处于冻结状态。正是这一点导致了WebWorker技术的出现—WebWorker允许在后台执行脚本,而与主线程之间则通过消息机制进行通信。这样一来,多线程编程就被引入浏览器。

以上这些API,有一些是在AngularJS 1.x开始开发之后才发明出来的,这就是为什么在AngularJS 1.x中并没有用到它们中的大部分内容的原因。但是,把这些API暴露给开发者可以带来很多好处,例如:● 性能显著改善。● 开发出来的软件质量更好。

现在,我们来简要讨论一下:如何在全新的Angular内核中融合上面提到的这些技术?为什么要这样做?ECMAScript的进化

现在,浏览器厂商们都在以非常短的迭代周期不断发布新特性,用户会经常收到升级通知。这种状况也让开发者能够使用最前沿的技术改善web现状从而推动web的进化。ES2015已经标准化,很多主流浏览器已经开始支持最新版的语言标准。作为开发者,学习和使用新语法不仅可以提升开发效率,而且也可以为不久的将来所有浏览器都支持新语法的那一天做好准备。所以,从现在就开始学习使用新语法变得很有必要。

某些项目可能会强制我们支持旧浏览器,而这些浏览器不支持任何ES2015新特性。在这种情况下,我们可以直接编写ECMAScript 5(ECMAScript 5标准发布于2009年——译者注),它与ES2015的语法虽然不同,但是语义上却是等价的。或者,我们可以利用预编译程序进行转码。我们可以利用ES2015新语法来编写代码,然后利用预编译程序编译成浏览器所支持的目标版本。

AngularJS发布于2009年。当时大部分网站的前端代码都只支持ECMAScript 3,这是ECMAScript 5发布之前的最后一个版本。这就意味着,那时候框架的实现语言当然就是ECMAScript 3。现在,如果要使用最新版的语言,就需要将整个AngularJS 1.x全部迁移到ES2015上去。

从一开始,Angular 2 就已经把web的现状考虑在内,所以这个版本的框架使用了最新的语法。Angular 2 是用ES2016的超集编写的(也就是TypeScript,稍后我们就来学习它),但是Angular 2也允许开发者使用自己喜欢的语言去写代码。如果不喜欢对代码做预编译处理并且想简化构建过程,可以直接使用ES2015,甚至使用ECMAScript 5。Web Component

Web Component草案首次公开发表于2012年5月22日,也就是AngularJS 1.x发布的三年之后。如前所述,Web Component标准允许我们创建自定义标签并增加行为。这一点听起来似曾相识,因为在AngularJS 1.x应用中,我们已经在使用类似的概念开发用户界面了。Web Component听起来就像是Angular指令的替代品,但是它的API更加直观、功能更加丰富,而且有浏览器的内置支持。它还带来了很多其他优点,例如更好的封装。这一点非常重要,因为良好的封装可以有效地处理CSS样式冲突问题。

如果要在AngularJS 1.x中增加对Web Component的支持,一种可行的策略就是:修改原有的指令实现,并在DOM编译器中引入新的原语。作为Angular开发者,我们都知道指令API有多么强大而复杂。它涉及非常多的内容,如postLink、preLink、compile、restrict、scope、controller等,当然,还有我们最爱的transclude。既然Web Component标准已被接受,未来众多的浏览器都将会为它提供底层实现上的支持,从而带来很多好处,例如更好的性能和native API支持。

在实现Web Component的过程中,众多web技术专家遭遇了Angular团队在开发指令API的时候所遇到过的相同难题,而最终解决方案却是英雄所见略同。Web Component的背后有着众多设计方面的优秀决策,其中就包括content标签,它可以用来解决AngularJS 1.x里面声名狼藉的transclusion(嵌入)问题(transclusion机制的作用是把HTML片段嵌入到模板里面,或者把模板嵌入到普通的HTML标签里面去。transclusion机制的用法极其烦琐而且难以理解,声名狼藉一词形容得还是比较中肯的——译者注)。既然指令API和Web Component解决的是同样的问题,只是解决方法有所不同,那么在Web Component之上再保留指令API就显得多此一举,而且增加了不必要的复杂性。这就是为什么Angular核心团队从一开始就决定在Web Component的基础上构建并全面支持新标准的原因。Web Component标准引入了一系列新特性,其中很多特性在部分浏览器上还没有实现。如果我们的应用在浏览器中运行,而浏览器却没有为某些新特性提供本地支持,那么Angular 2将会模拟这些特性。其中一个案例就是ng-content指令,它是content标签的填缝剂(polyfill)。(如果浏览器不支持某个新特性, polyfill的作用是模拟这种新特性从而抹平这一裂缝,现在有很多相关的小工具——译者注)。WebWorker

JavaScript以事件循环著称。一般来说,JavaScript 程序运行在单个线程里面,事件调度的策略是:把各种事件都push到一个队列里面,然后再按照事件到达的顺序依次处理。然而,当其中某个预定的事件需要占用大量运算时间的时候,这种调度策略就显得不那么高效了。处理这种事件将导致主线程阻塞,并且所有其他事件都得不到处理,直到这个耗时的运算结束,才能继续处理队列中的下一个事件。针对这种情况举一个简单的例子:点击鼠标触发一个事件,在事件的回调函数里面使用HTML5的音频API来做一些音频处理。如果处理的音轨非常大,而且算法所需要的计算量很多,那么整个UI就会冻结直到运算结束,这显然会影响到用户体验。

引入WebWorker API就是为了填这些坑。WebWorker允许在另一个线程里面执行计算密集型任务,从而解放主线程,让它可以处理用户输入并渲染用户界面。

那么,在Angular里面如何使用WebWorker呢?在回答这个问题之前,我们先来回顾一下AngularJS 1.x里面的一些工作原理。假设有一个企业级应用,用来处理海量数据,这些数据都要通过数据绑定机制渲染到屏幕上,我们应该怎么做?每绑定一块数据都会添加一个新的监视器(watcher)。一旦digest循环开始运行,它就需要遍历所有监视器,执行与之相关的表达,并把返回的结果与上一次遍历所获得结果做比较。这里有很多拖慢性能的地方:● 遍历大量的监视器(watcher)。● 在指定的上下文中执行表达式。● 复制返回值。● 把当前表达式的运算结果与上一次相比较。

以上所有步骤都有可能运行得非常慢,这和输入的数据量有关。如果digest循环涉及密集的运算,为什么不把它移到WebWorker中去?为什么不在WebWorker内部执行digest循环,获取到发生变化的数据绑定,然后再把它们应用到DOM上去呢?

为了达到这一目的,社区做了很多实验。然而,把这一机制融入到框架中并不容易。效果不尽如人意的一个主要原因是,框架和DOM操作紧紧耦合在一起。在监视器回调函数内部,Angular经常直接操作DOM,从而无法把监视器移到WebWorker中去,因为WebWorker是在独立的上下文中被调用的,无法直接访问DOM。同时,在AngularJS 1.x中,各个监视器之间存在各种隐式或者显式的依赖关系,这就要求digest循环执行多次才能获得稳定的结果。综合以上两点,结论就是:在主线程之外的独立线程里面监测改动很难获得成效。

如果在AngularJS 1.x 中处理这些问题,内部实现会变得相当复杂。因为框架一开始压根就不是基于这一机制构建的。而在Angular 2启动设计之前WebWorker已经实现标准化,所以核心团队从一开始就已经把它考虑在内了。从AngularJS 1.x中学到的经验

为了顺应潮流,框架不得不进行重新实现,在上一小节里面介绍了关于这一点的一些争论,但是有一点我们必须牢记:我们现在并非白手起家,我们拥有从AngularJS 1.x那里所学到的经验。自从2009年以来,并非只有Web在进化,我们也在开始构建越来越复杂的应用。今天,单页应用不再是标新立异之举,而更像是所有业务型web应用的标配。单页应用的定位是高性能和良好的用户体验。

利用AngularJS 1.x,我们已经可以构建高效、大规模的单页应用。然而,在大量的案例中使用之后,我们也发现了它的一些缺陷。为了满足这些新的需求,Angular核心团队从社区中吸取了大量经验,开始运用全新的思路来进行开发。在看到Angular 2提供的新特性的同时,我们应该看到它是根据AngularJS 1.x的经验发展而来的,然后再想一想,在过去的几年里面,那些困扰我们Angular开发者以及最终被解决掉的问题。Controller

AngularJS 1.x遵从了Model View Controller (MVC)的微架构模式。有人会争论说,它看起来更像 Model View ViewModel (MVVM),因为controller有自己独立的语法,而视图数据模型是作为scope或者当前上下文的属性而存在的。但是,如果我们使用Model View Presenter模式(MVP)也能实现同样的目的。由于可以用各种不同的形式来组织应用逻辑,所以核心团队把AngularJS 1.x称为Model View Whatever (MVW)框架。

在任何AngularJS应用程序中,视图(View)都应该是由指令组成的。各种指令互相协作,从而实现功能完整的用户界面。服务(Service)负责封装应用的业务逻辑。在服务代码中,我们可以通过HTTP与 RESTful 服务进行通信,使用WebSocket甚至使用WebRTC进行实时通信。对于我们的应用来说,服务是实现领域模型和业务规则的基础构件。还有另外一个组件就是控制器(Controller),它主要负责处理用户输入并把执行过程代理给对应的服务。

虽然服务和指令都有明确的角色定义,但是在iOS应用中,我们常常会看到Massive View Controller这种反模式。有时候,开发者会尝试在控制器中访问甚至直接修改DOM。一开始的时候,这种方式用来实现一些很简单的功能,例如修改标签的大小,或者快速粗暴地修改标签的样式。另一个值得注意的反模式就是:在不同的控制器中重复实现相同的业务逻辑。开发者倾向于复制粘贴这些逻辑,而实际上这些东西应该封装到service里面去。

构建AngularJS应用的最佳实践是:控制器根本不应该操作DOM,而是应该把访问和操作DOM的逻辑分离到指令中去。如果控制器之间有一些重复的逻辑,最大的可能就是:我们需要把这些逻辑封装到某个服务里面,如果某个控制器需要用到这些功能,就使用AngularJS的依赖注入机制注入这个服务。

以上就是我们从AngularJS 1.x中所学习到的内容。这样看来,似乎控制器的功能应该移到指令内部的控制器中去。由于指令支持依赖注入API,所以在接收到用户的输入之后,可以直接把具体的操作代理给注入的服务来执行。基于这一原因,Angular 2中采用了完全不同的实现方案,删除了ng-controller指令,解决了滥用该指令导致控制器满天飞的情况。在第4章Angular 2组件和指令入门中,我们将学习如何用Angular 2中的组件和指令来取代AngularJS1.x中控制器的功能。Scope

AngularJS中的数据绑定机制是利用scope 对象来实现的。我们首先在scope对象上添加各种属性,然后在模板中显式声明需要绑定这些属性(单向绑定或者双向绑定都可以)。这种方案看起来很清晰,但是scope还有两个更重要的职责:派发事件和实现基于脏值检测的行为。Angular初学者需要花费大量精力去理解什么是scope以及怎么使用scope。所以,AngularJS 1.2引入了一个叫controller as syntax的概念。它允许我们直接在控制器内部为当前上下文(this)添加属性,而不需要显式注入scope对象,然后再在上面添加属性。以下代码片段示范了这种简化的语法:

function MainCtrl() { this.name = 'Foobar';}MainCtrl.prototype.clicked = function () { alert('You clicked me!');};

Angular 2更进一步,直接删除了scope对象。所有表达式都在特定UI组件的上下文中执行。把scope API整体删掉之后使得Angular 2得到了大幅度简化,我们不再需要显式注入scope了,只要把属性直接添加到UI组件上,然后再进行绑定操作即可。这种API让人感觉更简单也更自然。

我们将在第4章 Angular 2组件和指令入门中详细学习组件和脏值检测机制。依赖注入

在JavaScript 领域,AngularJS 1.x也许是市面上第一个通过dependency injection (DI)引入inversion of control (IoC)机制的框架。DI可以带来很多好处,比如:易测试性、更好的代码结构和模块化,以及更简洁明了。虽然在1.x 版本中DI运行得相当不错,但是Angular 2对它进行了进一步的发挥。因为 Angular 2是基于最新web标准构建的,所以它使用ECMAScript 2016装饰器(decorator)语法对使用DI的代码进行了注解。这里的装饰器与Python中的装饰器或Java中的注解非常类似。它们都可以使用反射机制来decorate(装饰)指定对象的行为。由于装饰器还没有标准化,也不被主流浏览器所支持,所以使用的时候需要经过中间转换步骤。如果你不想这么麻烦,也可以直接用ECMAScript 5语法编写一些冗长的代码去实现相同的语义。

新版本的DI更灵活、功能更丰富,也消除了AngularJS 1.x中的一些误区,例如API不统一的问题。在 1.x中,有些对象是根据参数的位置顺序注入的(例如scope、标签、属性,以及指令link函数中的控制器);而其他对象则是根据名称注入的(例如在控制器、指令、服务和过滤器中会根据参数名称进行注入)。

在第5章 Angular 2中的依赖注入中,我们将进一步学习依赖注入API。服务端渲染

Web需求越大,web应用就变得越复杂。构建一个真实的单页应用需要编写大量的JavaScript代码,把用到的所有外部类库全部一次性包含进来会导致页面上脚本的体积增加到好几兆。在移动设备上初始化应用可能要用几秒到十几秒的时间:从服务端获取所有资源、解析并执行JavaScript、渲染页面、应用所有样式。如果在低端移动设备上使用无线网络,这个过程可能会让用户放弃访问应用。虽然可以用一些技巧来加速这个过程,但是在复杂的应用中,没有银弹。

在尝试提升用户体验的过程中,开发者们发现了所谓的server-side rendering(服务端渲染)技术。它可以把单页应用中所请求的某个视图在服务端渲染好,然后把对应的HTML直接发送给用户。随后,在所有资源处理完毕之后,脚本就会添加事件监听器并进行数据绑定操作。这样做看起来像是一个提升应用性能的好方法。使用此方法的先驱之一是ReactJS,它利用Node.js的DOM实现在服务端预先渲染用户界面。可惜的是,AngularJS 1.x 的构架不支持这种特性。原因是框架和浏览器API紧密耦合在一起,在WebWorker中进行脏值检测的时候我们也遇到过同样的问题。

服务端渲染的另一个典型使用场景就是:构建对Search Engine Optimization(SEO,搜索引擎优化)友好的应用。为了让AngularJS 1.x应用能够被搜索引擎索引,目前已经出现了很多hack方法。例如,其中一种实战案例是这么处理的:使用无前端浏览器漫游整个应用,执行每个页面上的脚本并把渲染结果缓存成HTML文件,从而让搜索引擎能够访问应用。

虽然这种变通方案可以构建对SEO友好的应用,但是采用服务端渲染技术可以同时解决之前提到的两个问题:一是提升用户体验;二是用更简单优雅的方式来构建对SEO友好的应用。

只要把Angular 2和DOM进行解耦,我们的应用就可以在浏览器之外的环境中运行了。为了实现这一目的,社区已经开发了一款工具,它允许我们首先在服务端预先渲染单页应用中的视图,然后再转发给浏览器。本书在编写这段内容的时候,这款工具仍然处在开发的早期阶段,所以它并没有被包含在框架的内核中。在第8章开发体验与服务端渲染中,我们将会深入学习这款工具。大规模应用

自从Backbone.js出现之后 ,MVW就是构建单页应用的标配。我们可以按照注意点分离原则把业务逻辑从视图中分离出来,从而构建出设计良好的应用。MVM可以使用观察者模式监听数据模型的改变,当发生改变的时候刷新视图。但是,其中的事件处理器之间存在一些显式或者隐式的依赖,这就使得应用中的数据流不清晰且难以理解。在AngularJS 1.x中,不同的监视器之间可以相互依赖,从而导致了digest循环必须进行若干次遍历,这些表达式的结果才能最终趋于稳定。所以,Angular 2 采用了单向数据流设计,其具备如下优点。● 更明确的数据流。● 不同的数据绑定之间没有依赖关系,所以digest没有存活时间(TTL)的概念。● 性能更高:● digest循环只运行一次。● 创建对immutable/observable (不可变/可观察)数据模型友好的

应用程序,从而可以做深度优化。

数据流的改变为AugularJS 1.x基础构架带来了又一项根本性的变革。

当需要维护一个用JavaScript编写的庞大的代码库时,我们可能要换一个角度来看数据流的问题。虽然JavaScript的鸭子类型(指js对象的动态特性——译者注)让这门语言非常灵活,但是同时也让IDE和文本编辑器很难对代码进行分析和支持。对大型项目进行代码重构变得很难而且容易出错,原因是在大多数情况下进行静态分析和类型推断是不可能的。同时,在缺少编译器的情况下,很容易出现错别字,在跑测试用例或者真正运行应用之前很难发现这些错误。

Angular核心团队决定使用TypeScript ,因为它有更好的工具,还有编译时类型检查;使用TypeScript有助于提高生产效率,还能减少出错。如上图所述,TypeScript是ECMAScript的超集,它引入了显式类型注解和编译器。TypeScript代码会被编译成当前浏览器所支持的普通的JavaScript。TypeScript从1.6版开始,已经实现了ECMAScript 2016装饰器,它是Angular 2的完美选择。

各种IDE和文本编辑器都可以更好地对TypeScript进行静态代码分析和类型检查。所有这些优点都可以减少出错的概率,从而极大地提高生产率,同时还可以简化代码重构过程。TypeScript另一个重要的隐含优点是使用静态类型带来的性能提升,因为JavaScript虚拟机可以对静态类型进行运行时优化。

在第3章 TypeScript速成中我们将详细讨论TypeScript。模板

模板是AngularJS 1.x的核心特性之一。模板是简单的HTML,并且不需要中间的处理和编译过程,这一点与mustache之类的大多数模板引擎不同。AngularJS中的模板简洁而强大,我们可以在模板内部创建Domain Specific Language(DSL,领域建模语言)来扩展HTML,还可以使用自定义标签和属性。

当然,这也是Web Component背后的主要目标之一。前面我们已经提到过Angular 2怎样使用这一新技术,以及为什么要使用它的原因。尽管AngularJS 1.x 中的模板很强大,但是还有很大的改进空间!Angular 2中的模板吸取了上一个版本中的精华,通过解决一些让人困惑的问题增强了模板的功能。

假设我们创建了一个指令,允许用户通过标签的attribute给它传递一个成员属性。在AngularJS 1.x中,有以下三种不同的实现方法:

如果我们有一个user指令,当需要给它传递name属性时,有三种不同的方法可以实现(这里的意思看起来和上一段的末尾有一点重复,原文如此——译者注):第一种方法是传递一个字面量(在这个例子里面,也就是"literal");第二种方法是传递一个字符串,这个字符串可以当成表达式来执行(在这个例子里面,也就是"expression");第三种方法是在{{}}中传递一个表达式。应该使用哪一种语法完全由指令的具体实现来决定,这就使得指令的API变成一团乱麻并且难以记忆。

在日常工作中,处理大量基于不同的设计方案而开发的组件是一件令人沮丧的事情。为了解决这些问题,我们需要引入一种通用的约定。但是,为了取得良好的结果并保持API的一致性,需要整个社区达成一致。

Angular 2为属性提供了特殊的语法来解决这个问题,属性值会在当前组件的上下文中执行,同时为传递字面量提供了不同的语法。

根据从AngularJS 1.x中获得的经验,还有一件事情我们已经习惯了,那就是模板指令里面使用的微语法(microsyntax),如ng-if、nf-for。举个例子,在 AngularJS 1.x中,如果需要遍历一个用户列表并展示用户姓名,我们可以这样做:

{{user.name}}

虽然这种语法看起来很直观,但是只有有限的工具能支持它。所以,Angular 2引入了更明确的语法来解决这个问题,同时语义上也更丰富:

以上代码明确定义了一个(user)属性,这个属性将会在迭代(users)的上下文中创建。

但是,这种语法在输入的时候显得太冗长。所以,开发者可以使用以下简化语法,然后再编译成更冗长的形式:

  • {{user.name}}
  • 文本编辑器和IDE可以为改进型的新模板提供更高级的工具支持。在第4章Angular 2中的组件和指令中,我们会讨论Angular 2中的模板。脏值检测

    在关于WebWorker的小节中,我们已经提到过:在WebWorker实例化出来的其他线程上下文中运行digest循环的时机。利用JavaScript虚拟机的代码优化机制可以获得显著的性能提升,其中一种优化叫作内联缓存(http://mrale.ph/blog/2012/06/03/ explaining-js-vms-in-js- inline-caches.html)。但是AngularJS 1.x中实现的digest循环内存使用效率不高,而且阻碍了这种优化过程。Angular团队在这方面做了许多的研究,发现了提升digest循环性能和效率的各种方法。这些发现推动了全新的脏值检测机制的开发。

    为了获得更大的灵活性,Angular团队把脏值检测机制提取了出来,并且与框架内核进行了解耦。这样一来就可以开发出不同的脏值检测策略,在不同的环境中可以采用不同的策略。

    最终结果就是:Angular 2中有两种内置脏值检测机制。● 动态脏值检测:与AngularJS 1.x中的脏值检测机制类似,用于不

    允许eval()的系统中,如CSP插件和Chrome插件。● JIT脏值检测:运行时动态生成脏值检测代码,允许JavaScript虚

    拟机进行深度代码优化。

    在第4章 Angular 2组件和指令入门中,我们将会学习新的脏值检测机制以及它们的配置方法。本章小结

    在本章中,我们谈到了Angular核心团队做出当前这种决策背后的主要原因,以及最新的两个主要版本缺乏向下兼容性的问题。我们可以发现,做出这些决策有两根导火线:一是Web的进化和前端开发的变革;二是从开发AngularJS 1.x 应用中得到的教训。

    在第一小节中,我们学习了为什么需要使用最新版的JavaScript语言,为什么要使用Web Component和WebWorker,以及为什么不值得在1.x版本中整合所有这些强大的工具。

    我们注意到了目前前端开发的发展方向,包括在最近几年里学到的经验。我们描述了为什么Angular 2要删除控制器和scope的概念,为什么要改变AngularJS 1.x架构,从而可以使用服务端渲染机制来构建对SEO友好的、高性能的单页应用。我们还讨论了另一个基本的话题,那就是构建大规模应用,以及这种情况为什么会导致框架使用单向数据流,为什么会选择TypeScript这种静态类型的语言。

    在下一章中,我们将会学习 Angular 2 应用中的主要构件、如何使用它们,以及它们之间的依赖关系。Angular 2复用了AngularJS 1.x中引入的组件命名机制,但是整体上已经彻底修改了单页应用的基本构件。我们会简要看一下新的组件,然后与老版本中的组件进行比较。我们会快速介绍指令、组件、路由、管道以及服务,然后再描述一下如何把它们整合在一起构建出优雅的单页应用。第2章Angular 2应用的基础构件

    在上一章中,我们谈到了Angular 2设计决策背后的驱动力,描述了开发这样一款全新框架的主要原因。Angular 2在基于Web标准构建的同时保留了上一个版本中的各种经验。尽管我们对Angular 2产生的主要的驱动力已经很熟悉,但是还没有来得及解释一下Angular 2中的一些核心概念。Angular 2最近发布的主要版本已经与AngularJS 1.x的设计思路完全不同了,对构建单页应用的各种基础构件进行了大规模的修改。

    在这一章中我们将会学习框架的核心内容,同时简单介绍一下Angular 2的主要组件。本章的另一个主要目标是:综合介绍一下如何把这些概念整合到一起,为web应用构建出专业级的用户界面。接下来的几个小节会对本书中将要学习的全部内容做概要介绍,其中的细节会在后续章节中详细解释。

    在本章中我们将会学到:● 对框架做概念性的简介,说明各种概念之间的关系。● 如何把各种组件组合在一起构建出用户界面。● Angular 2中指令的演进路线图,以及与上一个主版本相比,它

    的接口做了什么样的修改。● 强制进行注意点分离的原因,其导致了指令被分解成了两个不同

    的组件。为了更好地理解这两个组件,我们会示范定义这两个组

    件的基本语法。● 概要介绍新的脏值检测机制,以及它与指令所提供的执行上下文

    之间的关系。● 什么是分区,为什么它们能简化日常的开发流程。● 什么是管道以及它们与AngularJS 1.x过滤器之间的关系。● 什么是Angular 2的全新的依赖注入机制(DI),以及它们与服务

    组件之间的关系。Angular 2概念性简介

    在开始深入学习Angular 2中的各种内容之前,我们先来做一个概念性简介,看看各种概念是如何整合在一起的。请看下图:图1

    图1到图4说明了Angular 2中的主要概念以及这些概念之间的联系。这几张图的主要用途是:展示Angular 2中用来构建单页应用的各种核心模块以及这些模块之间的关系。图2

    Component(组件)是Angular 2中创建用户界面的主要构件。Component直接继承自Directive,它是给DOM附加行为的始祖。Component继承了Directive,扩展了更多特性,例如增加了视图的概念,可以在视图上绑定模板,从而可以渲染各种指令的组合体。视图模板内部可以使用各种表达式。

    上图示意了Angular 2中脏值检测机制的概念。脏值检测机制会执行digest循环,在特定UI组件的上下文中执行所注册的各种表达式。由于Angular 2删除了scope的概念,所以表达式的执行上下文是绑定在组件上的控制器。

    可以使用Differ(差异比较器)来优化Change Detection(脏值检测)机制,这就是为什么上图中这两个元素之间有一条连线的原因。

    Pipe(管道)是Angular 2中的另一个组件。我们可以把管道看成AngularJS 1.x中的过滤器。管道可以与组件组合使用。我们可以在表达式中使用管道,可以在任意组件的上下文中定义管道:图3

    我们再看一下上面的图。Directive和Component会把业务逻辑委托给Service。这样做可以更好地保证注意点分离、可维护性,以及代码的可复用性。通过框架提供的DI(依赖注入)机制,Directive可以接收到特定服务实例的引用,然后把业务逻辑代理给服务来执行。Directive和Component都可以使用DI机制,不仅可以注入Service,还可以注入DOM标签或者其他Component和Directive。

    最后,基于组件的路由机制可以用来定义应用程序中的路由。由于Directive里面不会包含模板,所以只有Component可以被路由渲染,Component代表了应用程序中不同的视图。路由中也可以使用预定义的各种指令,从而可以在不同的视图之间定义超链接,还可以定义组件应该渲染到哪个容器中。图4

    现在,我们来更加详细地解释一下这些概念,看一看在Angular 2应用中它们是如何相互协作的,同时看一看与它们AngularJS 1.x中的前辈相比做了什么修改。脏值检测

    为了支持单页应用,AngularJS 1.x引入了指令的概念。指令的用途是封装与DOM相关的逻辑,通过扩展HTML的语法和语义来构建组件,然后把这些组件组合到一起构建出用户界面。刚开始的时候,如同大多数创新概念一样,指令的概念饱受争议,因为它鼓励我们用自定义标签的方式来编写不合法的HTML,也鼓励编写不带data-前缀的属性。然而,随着时间的推移,人们逐渐接受了这个概念,并证明了它会一直存在下去。

    AngularJS 1.x中指令实现的另一个缺点是:我们可以用各种不同的方式使用它们。这就要求我们理解指令的属性值,这些值可能是:字面量、表达式、回调函数,或者微语法(microsyntax)。这样一来,要让开发工具来支持这些特性基本上是不可能的。

    Angular 2保留了指令的概念,吸取了AngularJS1.x中的精华,然后添加了一些新的思想和语法。AngularJS中指令的主要用途是:利用ES2015中的类编写自定义的逻辑,从而给DOM标签扩展新的行为。我们可以把这些类看成是关联在指令上的控制器,把构造函数看成类似AngularJS 1.x指令中的link函数。但是,新指令的可配制性是受限的。在定义模板的时候不允许使用新的指令,这就导致了现有的用来定义指令的各种属性没有必要存在。但是,简化版指令API并不会限制它们的行为,只是为了更强地贯彻注意点分离原则。为了给这个简化版的指令API提供一些补充,Angular 2为自定义UI标签引入了更加丰富的接口,称为Component(组件)。Component扩展了指令的功能,利用Component元数据,组件可以带有模板。后面我们会进一步学习组件相关的内容。

    Angular 2中的指令语法使用了ES2016装饰器。当然,我们也可以使用TypeScript、ES2015,甚至ECMAScript 5 (ES5)来达到同样的目的,只是要多敲几下键盘而已。以下代码使用TypeScript定义了一个简单的指令: @Directive({ selector:'[tooltip]' }) export class Tooltip{ private overlay:Overlay; @Input() private tooltip:string; constructor(private el:ElementRef, manager:OverlayManager){ this.overlay=manager.get(); } @HostListener('mouseenter') onMouseEnter(){ this.overlay.open(this.el.nativeElement, this.tooltip); } @HostListener('mouseleave') onMouseLeave(){ this.overlay.close(); } }

    在模板中可以利用以下标签来使用这一指令:

    Tell me the answer!

    当用户鼠标移动到标签,也就是Tell me the answer!上面的时候,Angular将会调用定义在指令装饰器@HostListener中的方法。最后,overlay上的open方法就会被执行。虽然我们可以在同一个标签上使用多个指令,但是最佳实践表明应该把标签的attribute当成选择器来使用。

    换成ECMAScript 5语法,可以这样来定义以上指令: var Tooltip=ng.core.Directive({ selector:'[tooltip]', inputs:['tooltip'], host:{ '(mouseenter)':'onMouseEnter()', '(mouseleave)':'onMouseLeave()' } }) .Class({ constructor:[ng.core.ElementRef, Overlay, function (tooltip, el,

    试读结束[说明:试读内容隐藏了图片]

    下载完整电子书


    相关推荐

    最新文章


    © 2020 txtepub下载