AngularJS -- 提供者(Providers)
每个Web应用程序都是有多个对象组合、协作来完成任务的。这些对象需要被实例化,并且连接在一起进行工作。在AngularJS应用程序中,这些对象都是由injector 注入器服务自动进行实例化和组装的。而injector 注入器呢,它可以创建两种类型的对象: service 服务和 特殊对象。
特殊对象是遵守了指定的Angular框架的API的。这些对象可以是一个Controller,也可以是一个directive指令,也可以是filter 过滤器,或者animation动画。。。而我们的injector注入器需要知道怎么样去创建这些对象。那么你就要通过注册一个用于创建对象的方法,去告诉注入器到底这个对象该如何创建。
我们最常见的通过注册去告诉注入器如何创建对象的方法有四种: Value, Factory,Service , Constant(常量).一会儿我们来看一下如何在不同的场景下通过不同的方式去创建和使用一个服务service.
注意: 为了使注册器injector知道如何去创建和组装连接这些所有的对象,它就得需要一个注册的清单,食谱(list)。清单中的每个对象都有一个标示符,还有一个对如何创建这个对象的描述。每一个清单元素都是一个Angular的module模块。一个Angular模块又可以拥有一个或者多个的清单,也就是说,一个模块可以包含它说依赖的其他模块的信息。
一个Angular应用程序是由启动模块开始的,Angular会先创建一个injector 注入器的实例。进而去创建把Angular自己的核心的模块( “ng” module),以及他们所依赖的模块注册到这个注入器injector的清单中去。 之后,注册器injector就可以根据注册在它的list清单中的信息知道该如何去创建你要的对象了。。。
value清单元素(== 个人还是习惯叫提供者)
假设我们想要一个非常简单的叫做 “clientId”的服务,它可以提供一个远程应用API的认证信息的字符串。你应该这样去定义:
var myApp = angular.module(‘myApp‘, []); myApp.value(‘clientId‘, ‘a12345654321x‘);
注意,我们创建了一个叫做 myApp的模块,这个模块定义了一个清单,清单中指定了如何构建clientId服务,例子中是一个字符串。
下面来看看如何使用Angular的数据绑定去使用我们刚才定义的clientId服务。
<html ng-app="myApp"> <body ng-controller="DemoController as demo"> Client ID: {{demo.clientId}} </body> </html> // .......js ..... myApp.controller(‘DemoController‘, [‘clientId‘, function DemoController(clientId) { this.clientId = clientId; }]);
是不是很简单,是不是一看就懂。 *^_^*
在这个例子中,我们使用了Value清单元素去定义了一个叫做clientId的服务,当我们在Controller里面使用到的时候,我们就直接告诉注入器的清单,我们需要一个叫做clientId的服务,然后我们就得到了这个服务的实例了。当然,在这里,这个服务仅仅是一个字符串而已。。。。
工厂(Factory)清单元素(一个工厂方法的提供者)
Value清单元素是一个非常简单的元素,在我们创建比如服务的时候,这样是完全不够的。所以,现在该我们其他强大的清单元素登场了: factory 工厂清单元素。 它有以下能力:
1. 可以去使用其他的服务(依赖性)
2. 初始化服务。
3. 延迟/ 懒惰加载
工厂清单元素,使用一个可以有一个或者多个参数的函数方法(function) 去配置如何创建一个新的服务。它的返回值就是那个方法,它知道该如何去创建你要的那个服务。
特别注意: 在Angular中,所有的服务都是单例的哦!! 也就是说注入器只会去创建一次你调用的那个对象,然后以后你再来创建的时候,它直接给你一个引用就行了。而不会再去给你创建一个服务实例。。。
因为factory 工厂清单元素是一个非常强有力的提供者,所以我们可以把我们之前的使用Value清单元素去创建clientId服务的提供者改成这样:
myApp.factory(‘clientId‘, function clientIdFactory() { return ‘a12345654321x‘; });
不过需要注意的是,其实在我们之前的例子中,我们的clientId只是一个认证凭据,在那种情况下,还是Value提供者比较适合于它。我们这里只是为了演示,才把它使用工厂提供者来实现以下的。
我们再来讨论下,比方说,我能海鲜创建一个服务,也拉过来计算一个用于远程认证的加密的token。这个Token是由刚才我们的Value提供者所提供的值和一个我们秘密存储在本地的数据计算出来的:
myApp.factory(‘apiToken‘, [‘clientId‘, function apiTokenFactory(clientId) { var encrypt = function(data1, data2) { // NSA-proof encryption algorithm: return (data1 + ‘:‘ + data2).toUpperCase(); }; var secret = window.localStorage.getItem(‘myApp.secret‘); var apiToken = encrypt(clientId, secret); return apiToken; }]);
上面的例子中,我们看到了apiToken服务是如何通过工厂(Factory)提供者定义的,它需要依赖于clientId服务。它的实现是将我们ClientId服务的数据和存储在本地的数据进行加密后返回。
建议: 我们在定义服务的时候,最好还是使用”factory”后缀,虽然不是必须的,但是这样非常有助于以后的调试和使用。
服务(Service)清单元素
javascript的开发人员通常使用自定义类型来进行面向对象的开发。我们来看看下面这个例子:
function UnicornLauncher(apiToken) { this.launchedCount = 0; this.launch = function() { // Make a request to the remote API and include the apiToken ... this.launchedCount++; } }
请注意,我们的UnicornLauncher 是需要apiToken的,我们可以使用工厂提供者来满足UnicornLauncher对apiToken服务的依赖:
myApp.factory(‘unicornLauncher‘, ["apiToken", function(apiToken) { return new UnicornLauncher(apiToken); }]);
是不是感觉有点儿别扭啊! 正确的方法是使用Service 提供者去实现.
服务提供者生产服务的过程和Value提供者,Factory工厂提供者的方式类似,但是它是通过 new 操作符去调用的构造函数。当然,你也可以传递零个或者多个的参数进去,代表你这个服务队其它服务的依赖。
因为我们准备给我们的UnicornLauncher类型一个构造函数了,我们就可以把上面例子中使用Factory工厂提供者的方式改写成这样:
myApp.service(‘unicornLauncher‘, ["apiToken", UnicornLauncher]);
是不是简单多了!
Provider 清单元素
就像签名所述一样,Provider清单元素是一个非常核心的提供者类型,所有其它的清单元素(提供者)类型都是在Provider清单元素之上的一个语法糖,也就是做了一些包装。它是能力最强的清单元素,但是对于大多数的服务来说,它太强大了。
Provider清单元素在语法上是一个实现了 $get 方法的自定义类型。它是一个类似于我们的Factory工厂清单元素的工厂方法。事实上,如果你定义一个Factory清单元素,一个空的provider类型和 $get方法 会根据高级选项去设置你的Factory函数。
仅仅在当你想暴露一个在你的应用程序启动之前可以进行的配置API的时候,你可以使用Provider清单元素。一般不用,要用的话,通常用在你需要让你的两个应用程序只有些许不同的时候,你可以使用它来进行应用程序配置。
我们的unicornLauncher 服务真的是非常的有用的。默认情况下,我们的启动器运行的时候是没任何的屏蔽的。但是,有的时候,我们在启动的时候必须做一些防护。 这是非常非常有用的。如果我们在我们的启动器运行之前,做一些防护的话,对你的应用程序是非常有利的,我们可以这样来配置:
myApp.provider(‘unicornLauncher‘, function UnicornLauncherProvider() { var useTinfoilShielding = false; this.useTinfoilShielding = function(value) { useTinfoilShielding = !!value; }; this.$get = ["apiToken", function unicornLauncherFactory(apiToken) { // let‘s assume that the UnicornLauncher constructor was also changed to // accept and use the useTinfoilShielding argument return new UnicornLauncher(apiToken, useTinfoilShielding); }]; });
我们给我们的应用程序加上了防护,这个时候,我们需要创建一个配置功能模块,而且必须有一个UnicornLauncherProvider 注入进去才能正常启动:
myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) { unicornLauncherProvider.useTinfoilShielding(true); }]);
请注意, unicorn 被注入到config函数里了。它的注入器是通过提供者注入器(provider injector)注入的,而不是普通的注入器注入的。(接下来这句话不知道怎么翻译,上原文) in that it instantiates and wires (injects) all provider instances only.
在应用程序启动期间,在Angular创建所有的服务完毕之前,它会实例化和配置所有的提供者(providers)。我们把这个配置阶段叫做应用程序的生命周期。在这个期间,服务是不可和使用的,因为他们还没有被创建出来!
配置阶段结束后,则不再允许与provider作用(不允许作用,我的理解是不能再修改)了,然后创建服务的进程开始了.我们把这个阶段叫做应用程序生命周期的运行阶段。
Constant 常量清单元素
我们已经知道了如何将Angular的生命周期分成配置阶段和运行阶段。还有你可以通过config函数给你的应用程序进行配置.甚至如果没有服务是空的的话,config 配置函数运行在配置阶段,并且你是不可以通过Value清单元素去创建一个值对象的。
在配置阶段我们给unicornLauncher配置了名字。我们可以在应用程序的运行期间,通过Controller去使用它。我们要定义一个constant 可以这样做:
myApp.constant(‘planetName‘, ‘Greasy Giant‘);
我们可以配置unicornLauncherProvider为这样:
myApp.config([‘unicornLauncherProvider‘, ‘planetName‘, function(unicornLauncherProvider, planetName) { unicornLauncherProvider.useTinfoilShielding(true); unicornLauncherProvider.stampText(planetName); }]);
我们可以使用常量清单元素Constant创建了有效的常量,也可以在运行时期使用Value值清单元素去配置,我们可以再我们的Controller和模板(template)中这样使用:
myApp.controller(‘DemoController‘, ["clientId", "planetName", function DemoController(clientId, planetName) { this.clientId = clientId; this.planetName = planetName; }]);
<html ng-app="myApp"> <body ng-controller="DemoController as demo"> Client ID: {{demo.clientId}} <br> Planet Name: {{demo.planetName}} </body> </html>
特殊目的的对象
我们提到过,我们也会需要给我们不同的服务创建一些特殊目的的对象。这些对象作为一种插件来扩展我们的框架,那么这样的对象就需要实现一些Angular指定的接口。这些接口可以使controller,directive,Filter和Animation.
下面我们来介绍如果使用injector去创建这些特殊对象(除了Controller对象以外)。然后在后台使用Factory工厂清单元素进行绑定。来看看我们是如何通过刚刚我们定义的plaetName常量使用Angular的指令去创建一个非常简单的组件的。
我们已经知道了,我们可以通过Factory工厂清单元素去注册一个指令,我们可以使用于Factory相同的语法去做:
myApp.directive(‘myPlanet‘, [‘planetName‘, function myPlanetDirectiveFactory(planetName) { // directive definition object return { restrict: ‘E‘, scope: {}, link: function($scope, $element) { $element.text(‘Planet: ‘ + planetName); } } }]);
然后,我们可以这样去使用这个组件:
<html ng-app="myApp"> <body> <my-planet></my-planet> </body> </html>
使用Factory工厂清单元素,你还可以定义AngularJS的filter(过滤器)和animation(动画),但是如果是想创建Controller的话,就有点特别了。你创建一个自定义类型的Controller,你可以再它的声明中指定它的构造函数中所依赖的对象类型。这个构造函数会被注册到模块中去。让我们来看看一个我们很早之前的例子:
myApp.controller(‘DemoController‘, [‘clientId‘, function DemoController(clientId) { this.clientId = clientId; }]);
在应用程序当中,每次我们需要实例化一个DemoController(在我们的例子中,只实例化了一次)的时候,都会通过它的构造函数去实例化一个DemoController 对象。 所以它不像是服务(service)一样,是单例模式的。在它的构造函数中可以调用服务,我们的这个例子调用的是dlientID服务。
总 结:
1. 注入器(injector)使用清单中的元素来创建两种对象:服务科特殊对象。
2. 在我们的清单中,一共有五种类型的清单元素: Value, Factory, Service, Provider, Constant。
3. Factory和Service清单元素是最常用的。他们之间的唯一区别是,Service清单元素更加适合自定义类型的对象的创建,而Factory清单元素可以使用JavaScript的基元和功能。
4. Provider清单元素是一个非常核心的清单元素类型,而且所有其他的清单元素类型都是基于它的语法糖而已。
5. Provider是最复杂的清单元素类型。除非你确实需要构建一个可重用的,一个全局通用的代码的时候,否则你是不需要它的。
6. 所有的特殊对象的定义中,只有Controller的定义是通过Factory清单元素进行配置的。
特性/清单元素类型 | Factory | Service | Value | Constant | Provider |
是否可以有依赖项 | yes | yes | no | no | yes |
支持使用类型注入 | no | yes | yes* | yes* | no |
对象在配置(config)阶段有效 | no | no | no | yes | yes** |
可以创建函数(function) | yes | yes | yes | yes | yes |
可以创建基元 | yes | no | yes | yes | yes |
* 代表需要你使用new操作符去直接初始化
** 代表在config 配置阶段是无效的,但是Provider实例是有效的(看上面的unicornLauncherProvider例子)
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。