Angular 2 中的核心概念

摘要: Angular 2 中的核心概念

翻译/大漠穷秋

 

原文点这里Victor Savkin 是Angular核心团队成员,路由模块就是他开发的。

在这篇文章中我将会讨论Angular 2 中的3个核心概念:组件、依赖注入,以及数据绑定。

 

我们来构建一款App

比方说我们打算构建这样一款应用,里面有一个技术讲座列表,你可以进行过滤、查看、打分操作。

 

组件

在构建Angular 2 应用的时候,你需要针对每一个UI元素、屏幕以及路由定义出一系列的组件。应用一定会有一个根组件,用来容纳其它所有组件。换句话说,每一个Angular 2 应用都会拥有一颗组件树,当前这款应用的组件树是这样的:

其中,Application是根组件;Filters组件带有一个名为speaker的输入框,还带有一个filter按钮;TalkList就是你在下面看到的讲座列表;TalkCmp就是列表里面的一个项目。

为了理解Angular 2 组件的构成,我们来仔细看一看TalkCMP组件。

TalkCmp.ts:

Talk_cmp.html

 

输入和输出属性

每一个组件都可以带有输入和输出属性,可以定义在组件装饰器(decorator )中,也可以用属性装饰器定义。

数据通过输入属性流进组件,通过输出属性流出。

输入和输出属性都是组件的public API。当你在应用里面实例化组件的时候可以使用这些属性。

你可以使用属性绑定(方括号)来设置输入属性的值,通过事件绑定(圆括号)来订阅输出属性。

组件可以带有模板,用来描述组件在页面上的渲染方式。

Talk_cmp.html

为了渲染模板,Angular需要知道两件事情:模板里面可以使用的指令列表;以及模板本身。你可以把模板定义在组件外部,然后使用templateUrl属性引入,就像上面的例子这样;也可以内联在组件内部,像下面这样:

 

生命周期

组件拥有定义良好的生命周期,你可以接入到其中。TalkCmp组件没有订阅任何生命周期事件,其它组件可以根据需要订阅事件。例如,当输入属性发生变化的时候下面这个组件就会收到通知:

 

Providers(提供者)

组件可以包含一个providers列表,组件或者组件的孩子都可以注入列表中的类。

在这个例子里面,我们把backend和logger服务定义在根组件里面,这样在整个应用里面都可以使用这两个服务。TalksCmp组件注入了backend服务。我会在本文的第二部分来详细讨论依赖注入,现在你只要记住组件可以通过配置项进行依赖注入就可以了。

 

宿主元素

为了让Angular组件能渲染到DOM里面,你需要把它与某个DOM元素关联起来。我们把这种元素叫做宿主元素。

组件可以通过以下几种方式来和它的宿主DOM元素进行交互:

● 组件可以监听宿主元素上的事件。

● 组件可以刷新宿主元素的属性。

● 组件可以调用宿主元素上的方法。

例如,以下组件会利用HostListeners来监听input事件,然后对获取到的值进行trim操作,并把最终的值存储到某个字段上。然后Angular就会把存储的值同步到DOM上。

注意,这里我没有直接和DOM进行交互。Angular 2 致力于提供一种高层次的API,这样一来原生平台,也就是DOM,只是用来反映Angular应用的状态而已。

这样做是非常有用的,原因如下:

● 组件更加易于重构。

● 这样可以在不触及DOM的情况下对应用的大多数行为进行测试。这种测试更加容易编写并易于理解。另外,这些测试运行起来非常快。

● 这样做可以让Angular应用运行在web worker中。

● 这样可以让Angular应用完全脱离浏览器环境并运行在其它平台上,例如,使用NativeScript。

有时候,你必须要直接与DOM进行交互。Angular 2 也提供了这样的API,但是我们希望你尽量不要使用它们。

 

组件是自我描述的

组件是由下列成份所构成的:

● 组件知道如何与它的宿主元素进行交互。

● 组件知道如何渲染自己。

● 组件里面可以配置依赖注入。

● 组件拥有定义良好的输入和输出属性,它们是public API。

所有这些让Angular 2 中的组件拥有自我描述性,它们包含了实例化所必须的所有信息。这一点非常重要。

这就意味着,任何组件都可以被当成应用而启动,而不需要进行任何特殊处理。另外,任何组件都可以被加载到路由的outlet里面。这样做的结果就是,你所编写的任何组件都可以被当成应用来启动,也可以被路由加载,或者直接在其它组件里面使用。这样需要学习的API更少。同时也让组件的可复用性更高。

 

指令呢?

如果你熟悉Angular 1,你必然会有一个疑问:“指令去哪儿了?”。

实际上Angular 2 里面依然存在指令。组件是最重要的一种指令,但不是唯一的指令。组件是带有模板的指令。当然你还可以继续编写基于装饰器的指令,这些指令可以不带有模板。

 

小结

组件是Angular 2 应用中最重要的基础构件。

● 它们拥有定义良好的输入和输出。

● 它们拥有定义良好的生命周期。

● 它们是自我描述的。

 

 

依赖注入

我们换一个话题,来讨论一下Angular的另一块基石---依赖注入。

依赖注入背后的思路非常简单。如果你有一个组件,依赖于一个服务。你不需要自己创建这个服务的实例。相反,你可以在组件的构造函数里面请求这个服务的一个实例,然后框架就会为你提供一个。这样一来你就可以依赖于接口而不是具体的类型了。这样可以让代码更加松耦合,而且可测试性更佳,同时还可以实现其它一些很好用的特性。

Angular 2 内置了依赖注入模块。为了理解它的用法,我们来看以下组件,这个组件会使用for指令来渲染talk列表:

talks.html

我们创建一个假的服务来提供数据。

这个服务我们怎么用呢?一种方法是在组件里面创建这个服务的一个实例。

对于一个demo app来说这没有什么问题,但是对于真实的应用来说这样是不行的。在真实的应用里面,TalksAppBackend并不会直接返回一个数组就完事,它会发起http请求去获取数据。这就意味着,如果我们对这个组件进行单元测试,那就必须发起真实的http请求,而这并不是一个好主意。这里的问题就在于,我们用new操作符把TalkList与TalksAppBackend 紧密耦合到了一起。

为了解决这个问题,我们可以把一个TalksAppBackend实例注射到TalkList的构造函数里面,这样在测试的时候我们就可以很方便地替换它了,比如这样:

这段代码告诉Angular,TalksList会依赖于TalksAppBackend服务。现在,我们必须告诉Angular如何去创建TalksAppBackend的实例,我们可以在组件的providers属性里面加上TalksAppBackend的声明。

TalksAppBackend服务必须在TalkList组件或者它的祖先里面声明。所以,如果你更喜欢用Angular 1的风格来编写应用,你可以把所有provider都配置在根组件里面。这样在系统中的任何组件里面都可以使用它们。

 

单一API

Angular 1 和Angular 2 都有依赖注入模块。但是在Angular 1 里面有很多API可以把依赖注入到指令中:有些对象是根据位置进行注入的(例如DOM元素);有一些是根据名称进行注入的。这样难免让人感到有些困惑。而Angular 2 提供了唯一一个API用来注入依赖,所有依赖都注入到组件的构造函数里面。

例如,下面这个组件注入了一个TalksAppBackend(它很可能是一个单例);还注入了一个ElementRef,它在每个组件里面都是不同的实例。

 

所以,无论注入全局依赖还是局部依赖,我们都会使用相同的API。另外,组件可以使用相同的API注入另一个组件。

 

小结

有一些特性你无法立即看到它的好处,依赖注入就是其中之一。但是,当你的应用越来越大的时候,这样的特性就会变得越来越重要。

● 它让你可以依赖于接口,而不是具体的类型。

● 可以让代码更加松耦合。

● 提升了可测试性。

● Angular 2 只有一个API用来给组件注入依赖。

 

 

属性绑定

Angular使用属性绑定机制来自动在组件树和数据模型之间进行同步,并且在DOM与组件树之间进行同步。为了理解这一点为什么很重要,再来回顾一下我们的应用:

 

我们知道,这款应用会有一颗组件树。除了组件树之外,它还会带有数据模型。假设数据模型是一个简单的JavaScript对象,比如这样:

现在,假设有一个事件修改了数据模型。比方说我已经看过了这段讲座,我真的很喜欢,然后我给它打了9.9分。

如果我必须自己去查找那些可能依赖于新值的地方,然后手动更新它们,那样代码会非常冗长并且很容易出错。我想让我的应用能自动反应数据模型的变化,这就是属性绑定的作用。

在VM加载结束之后,Angular会检查组件树中的每一个组件。更确切地说,Angular将会检查每一个属性绑定(每一个方括号,每一个双花括号),并且会刷新组件。同时,Angular还会刷新DOM,与组件树的状态保持一致。

只有组件的输入属性才可以用属性绑定进行刷新。

 

 

Zones

在Angular 1 里面,你必须自己调用scope.$apply告诉框架进行脏值检测。在Angular 2 里面你不需要再操心这一点了。Angular 2 里面使用了Zones,它知道什么时候需要进行脏值检测。这就意味着,当你集成第三方库的时候,再也不需要手动调用scope.$apply了。

 

小结

● Angular使用属性绑定机制来自动在组件树和数据模型之间进行同步,并且在DOM与组件树之间进行同步。

● 利用Zones,Angular知道什么时候需要进行脏值检测。

 

 

内容回顾

指令,尤其组件,是Angular中最重要的部分。它们是Angular 2 应用的基石。它们是自我描述的。它们自我描述自己的public API,也就是输入和输出。它们还会自我描述自己的private API,也就是生命周期钩子,包括它们与宿主元素交互的方式。某些组件可能会依赖于其它组件和服务,而依赖注入模块会提供这些功能。依赖注入模块会负责构建出组件树,这是Angular 2 应用的核心。最后,属性绑定和Zones让Angular 2应用变得可交互。

这些就是构成Angular 2 内核的基础构件。理解了这些概念你就可以开始使用Angular 2了,但是构建真实的应用还需要更多的内容。这就是为什么我们不断在内核的基础上构建一系列的模块用来保证良好的开发体验的原因。其中包括:

● 处理表单或者输入项的模块

● http客户端

● 强大的路由

● 动效支持

● UI套件,又叫material组件库(基于Google Material Design的UI组件库---译者注

● 用来编写单元测试、e2e(end to end,端到端测试---译者注),以及性能测试的工具

所有这些都已经准备完毕,可以用来构建应用了。

本文来源:https://my.oschina.net/mumu/blog/782453


如果给你带来帮助,欢迎微信或支付宝扫一扫,赞一下。