Angular2 和TypeScript
原文链接:https://www.infoq.com/articles/Angular2-TypeScript-High-Level-Overview
作者: Yakov Fain Posted on Apr 26, 2016
------------------------------------------------------------------------------------------------------------------------------
AngularJS是目前最流行的可用于创建网页应用的javascript框架。而且现在Angular2和TypeScript正在让真正的面向对象开发方式的成为网页开发的主流方式,而且在语法上与Java 8惊人的相似。
根据Google工程总监Brad Green介绍, 有130万开发人员在使用AngularJS且有30万开发人员已经准备好开始使用Angular2了。 在使用了Angular2将近十个月之后,我相信其对javascript社区的影响将能媲美Spring框架对于Java社区的影响。
在这篇文章中,我将会展示一个关于Angular2框架的简要概述。
在2014年末,google宣布Angular2将会对AngularJS进行完全的重写, 为了写Angular2的应用程序,他们甚至创建了一个全新的语言“AtScript”。
但同时, 微软答应要对他们的TypeScript语言(一个严格类型的Javascriptz超集语言)增加对装饰(亦称注释)的支持,这就为开发Angular2框架诞生了一个新语言,并且这是一门使用AngularJS框架开发应用的推荐语言。
你当然也可以用Javascript(ECMAScript5和6都可以)和Dart开发Angular 2应用。
此外,Angular团队集成了另一个微软的项目到Angular2框架中-- RxJS库,一个原生Javascript的扩展类库。
Angular2不是一个MVC框架,而是一个基于组件化的框架。在Angluar2种,应用是一个松散耦合的组件树。
举个栗子,下面这个截图展示了一个示例在线拍卖应用的登陆页面, 该页面是由导航栏、搜索框、轮转展示区、产品和底部这些组件组合而成的最初原型。
上面这张图片演示了3个产品呈现组件。自动呈现功能是通过在模板上绑定一个从服务端获取数据的组件数组。 每个产品的title是一个关联到产品详情页的链接。既然我们想将拍卖系统设计为一个单页面应用(SPA),我们就不希望为了展示产品详情而刷新整个页面。让我们重复利用现在被轮转区和产品列表占用的区域,它也可以用来呈现产品详情同时保持页面的其他区域不变。这个任务可以通过简单的几步完成:
1. 用Angular的路由-出口指令,这允许你将被轮转区和产品列表占用的区域定义为《路由出口》, 以便它可以通过用户的导航栏来改变内容。
2. 将轮转区和产品组件封装到主页组件里
3. 新建一个产品详情组件
4. 配置Angular的路由让其在特定的《路由出口》区域显示主页或产品详情组件。
我们已经说了很多关于组件的内容,但我们还没有明确定义什么是组件。在TypeScript中,一个组件就是一个简单的通过@Component关键字注释的类:
1 @Component({ 2 selector: ‘auction-home‘, 3 template: ` 4 HTML or other markup is in-lined here 5 ` 6 }) 7 export default class HomeComponent { 8 9 // Application logic goes here 10 }
@component注释是用来定义组件和相关的元数据的。 在这个例子中,selector属性的值定义了用于展现该组件的HTML标签名。template属性是HTML(或其他)标记的占位符。
回到我们的拍卖登陆页面,最高级别的应用组件模板看起来应该像是这样的:
这个模板由标准和自定义的html标签混合而成,自定义HTML标签标识各个组件。在例子中我们把html纵向排列了。如果我们希望把标记存储在不同的文件中(在这个例子中是application.html), 我们可以使用templateURL属性代替template属性, 然后ApplicationComponent的代码就会像这样:
import {Component} from ‘angular2/core‘; import {Route, RouteConfig, RouterOutlet} from ‘angular2/router‘; import HomeComponent from ‘../home/home‘; import NavbarComponent from ‘../navbar/navbar‘; import FooterComponent from ‘../footer/footer‘; import SearchComponent from ‘../search/search‘; import ProductDetailComponent from "../product-detail/product-detail"; @Component({ selector: ‘auction-application‘, templateUrl: ‘app/components/application/application.html‘, directives: [ RouterOutlet, NavbarComponent, FooterComponent, SearchComponent, HomeComponent ] }) @RouteConfig([ {path: ‘/‘, component: HomeComponent, as: ‘Home‘}, {path: ‘/products/:id‘, component: ProductDetailComponent, as: ‘ProductDetail‘} ]) export default class ApplicationComponent {}
ApplicationComponent类使用了@Component和@RouteConfig(用于标记为URL依赖内容)进行注释。selector属性的值会被用来指定用户自定义的HTML标签<auction-application>。 templateURL属性指定了标记的位置。section指令包含了路由出口和所有子级组件。
@RouteConfig注释为客户端导航栏配置了两个路由:
· 名字为Home(主页)的路由节点的内容会被HomeComponenet组件渲染并映射到URL片段‘/‘ 下。
· 名字为ProductDetail(产品详情)的路由节点会被ProductDetailComponent组件映射到URL片段‘/product:id‘下。
当用户点击一个特定的产品标题时,默认Home路由节点的内容会被ProductDetail路由节点的内容替换,该点击事件会提供参数id的值并将产品详情展示在路由出口区域。 例如,用于导航到ProductDetail路由节点的链接,带有一个产品ID参数,且值为1234, 看起来可以像下面这样:
<a [routerLink]="[‘/ProductDetail‘, {‘prodId‘: 1234}]">{{ product.id }}</a>
依赖注入
组件们使用服务来实现业务逻辑。服务就是些由Angular实例化并注入到组件中的类。
export class ProductService { products: Product[] = []; getProducts(): Array<Product> { // The code to retrieve product into goes here return products; } }
现在如果你将一个类型是ProductService的变量以参数形式传入到HomeComponent的构造器中,Angular会自动实例化并注入该服务到该组件中:
@Component{ ... } export default class HomeComponent { products: Product[] = []; constructor(productService: ProductService) { this.products = productService.getProducts(); } }
Angular的依赖注入模型是很灵活的,它很容易使用, 因为对象都只能通过构造器进行注入。注入器可以形成一个层(每个组件都有一个注入器),并且可注入对象不需要在应用级别被实现为单例模式,因为它默认就是单例的,就像在Spring中一样。
组件间通信
组件间通信可以并且应该以松耦合的方式来实现。 一个组件可以定义输入和输出属性。要从父组件传输数据给子组件, 父组件需要绑定子组件的输入属性的值。子组件不需要知道是谁提供了这些值,它只需要知道怎么使用这些值。
如果一个组件需要将数据传输给外界,它通过输出属性往外发送事件。 发送给谁? 这个组件就不用管了。 对该事件感兴趣的组件会对该自定义组件的事件创建侦听器。
这种机制允许我们将组件看作黑盒,我们可以把值传进去或发出来。我最近录制了一段可能对你有用的短片,该短片演示了在Angular2中实现中间人设计模式的一种方法。
为什么用TypeScript
TypeScript是Javascript的一个超集, 但又像Java一样允许你定义新的类型。 通过类型定义变量,而不是依然用原本的var关键字为新工具的支持开后门,你会发现这是一个巨大的生产力的提高。TypeScript带来了一个静态代码分析器,当你输入代码到你的可识别TypeScript的编辑器(如WebStorm/IntelliJ Idea,Visual Studio Code, Sublime Text等等) 对上下文敏感的智能提示功能会引导你给函数传入有效的参数,如让参数使用有效的对象或类型。如果你偶然使用了不正确的类型,编辑器会将错误的代码高亮显示。在这里看看WebStorm是如何支持TypeScript的.
即使你的TypeScript应用使用了第三方类库来编写Javascript,你也可以设置一个类型定义文件(使用.d.ts后缀名),包含该类库的类型定义。数百个流行的Javascript类库的类型定义文件可以免费获得,你可以很容易的通过Typings安装他们, Typings是一个TypeScript的定义管理工具。 想象一下你想要在你的TypeScript代码中使用jQuery(用Javascript编写)。jQuery的类型定义文件会包含所有jQuery的APIs定义(包含类型),所以你的IDE就可以提示你需要用哪些类型,或把错误的代码高亮。
性能和渲染
渲染性能在Angular2种得到了答复提高。最重要的是,事实上, 渲染模块已经位于一个独立的模块当中,该模块允许你在工作线程中运行运算量较大的代码。可以访问重绘速度挑战网站来比较各个框架的渲染性能。你能感受到不断更新的巨大数据网格的高速渲染。运行标题为“DBMON Angular 2.0测试版-网络工作者"的测试用例,一个包含大量数据的网格不断的刷新数据(在独立的线程中)并在浏览器中进行极快速的重绘。
如果你想问是什么功能让Angular2从其他框架中脱颖而出,出现在我的清单中的第一位将会是模板渲染的分区的独立模块:
· 具有组件界面定义的独立模板文件会通过一个独立的渲染器进行处理,这为模板的优化和预编译领域提供了新的机会,让其具有创建模板并渲染在不同的设备上的能力。
· zone.js模块会侦听应用的改变,并决定什么时候去更新每个组件的界面。 任何异步事件的触发都会重新验证每个组件的界面而且速度快的吓人。
注意:对于大多数应用,你不需要知道zone.js的内部原理,但如果你所在的项目要求优化一个复杂应用的界面渲染,你就需要准备好拨出一些时间去学习更多的zone的内部运作细节。
让渲染引擎保持在一个独立模块中并允许第三方类库替换原始DOM的渲染器,这能实现不依赖于浏览器平台的目标。例如,允许将应用代码跨设备重用,在移动设备中,界面渲染器就将使用原生组件。 TypeScript类的代码不需要改变,但@Component注释的内容会包含XML或其他语言,这些内容将用于渲染原生组件。一个自定义的Angular2渲染器已经在NativeScript框架中得以实现, 该渲染器提供的服务就像在Javascript、原生iOS和安卓界面组件之间架起一座桥梁一样。通过NativeScript你可以重用组件的代码,而你需要做的仅仅是将模板中的HTML替换为XML。 另一个自定义界面渲染器允许使用Angular 2 with React Native,这是一种完全不同的为iOS和安卓系统创建原生(不是hybird)界面的方法。
工具集
虽然Angular2的语法和架构比AngularJS 1.X更容易理解, 但Angular2的工具集却比后者更复杂一些。这并不让人惊讶;毕竟你正在用一种语言写代码并将其编译为另一种语言,因为所有东西都会被编译成Javascript。
Angular CLI现在是一个承诺了会提供命令行界面的项目,它能大幅简化各个流程,从项目最初开始到生产部署。
应用调试可以在编辑器或浏览器中完成。我们使用Chrome的开发者工具来进行调试。 Chrome生成出的源码地图允许你在浏览器运行Javascript时调试TypeScrip代码。如果你更乐于调试Javascript, 这也是可以的, 因为TypeScript转译器会生成Javascript代码,这就能让别人直接去读了。
测试和部署
Angular2自带了一个测试库, 该库允许你在BDD格式下编写测试用例。目前它仅支持Jasmine框架,但能支持更多的框架已经指日可待。我们使用Karma测试工具, 该工具允许针对不同的浏览器分别跑不同的测试用例。
Protractor框架允许你为你的应用编写端到端测试用例。如果你在开发模式下加载一个简单应用时监控你的网络,你会看到浏览器下载了超过5兆(这其中有一半是模块加载器要用到的TypeScript编译器,叫做SystemJS)。但在运行了部署和优化脚本(我们用的是Webpack打包工具)后,这个小应用的体积可以缩小到160K(包含了Angular2框架)。我们正期待着想看看Angular CLI会如何实现生产环境的打包功能。 Angular团队在实现离线模板编译功能,这能让框架的体积缩小到50Kb左右。
界面组件库
在我写这篇文章的时候,已经有少数几个界面组件库你可以在Angular2应用中使用:
· PrimeNG - 一个PrimeFaces(一个在Java服务端框架中使用的非常流行的库)的创造者编写的Angular2界面组件库。
· Wijmo 5 - 一个商业的Angular 2界面组件库。 你需要购买开发者证书才能使用它。
· Polymer - 一个Google开发的可扩展的非常漂亮的组件库。 在我们公司我们已经设法使用Polymer组件库创建一个Angular2的飞行员应用,但在两者的整合上还有提升的空间。
· Material Design 2 - 一个Google特别为了Angular2开发的界面组件库。 目前这个库还处于雏形阶段, 但是其发展得非常迅速, 而且我非常期待在未来的三到四个月内可以看到一大堆设计良好的界面组件。
· NG-Lightning - 一个Angular2的组件和指令库, 该库是在Lightning设计系统的CSS框架下用TypeScript写出来的。
在内部系统中使用Angular2稳妥吗?
从第一个公测版发布开始,我们在Farata系统使用Angular2来编写一个真实世界项目,而且并没有陷入任何大麻烦中, 至少没遇到找不到解决方法的问题。
如果你想更保险一点, 可以再等多几个月。小道消息说Angular2的RC版本(指有可能成为最终产品的候选版本)会在2016年5月的Google I/0大会上公布。
将来会发生什么?
2016年3月, 布拉德·格林通过O‘Reilly在Fluent大会上发表了一次主题演讲。 可以看看这次演讲的视频。 你被触动到了吗? 反正我被触动到了。
关于作者
Yakov Fain是一位住在纽约的Java拥护者,同时是IT咨询Farata系统的合作伙伴。 他领导着 Princeton JUG组织,他也在软件开发领域写过很多文章和几本书。最近他正与人共同创作《用TypeScript开发Angular 2》这本书, 这本书会在2016年6月筹备发布。Yakov经常在技术大会上发表演讲,同时还教授Java和Angular2课程。 他的博客是yakovfain.com。