angularjs provider的区别和使用

module

angular 可以对一个 module 使用.config 方法来配置这个模块, config 可以注入一个$provide对象, 所有的配置都是通过$provide 的方法调用的.
$provide 下有六个方法:

  • provider
  • factory
  • service
  • value
  • constant
  • decorator

它们都有自己的语法糖, 即不是通过.config(['$provide', function($provide){}])调用.

provider

provider是最重要的一个方法, 了解provider以后, 会发现factory, service, value三个方法只是provider的语法糖.

provieder的源码:

1
2
3
4
5
6
7
8
9
function provider(name, provider_) {
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw Error('Provider ' + name + ' must define $get factory method.');
}
return (providerCache[name + providerSuffix] = provider_);
}

name是这个 provider 的名称, 在整个 app 里面应该唯一, 否则会覆盖. 如果第二个参数是数组, 则按依赖注入前面的参数, 解析最后一个函数, 如果是函数, 则直接解析. 这两种解析方式函数返回的对象都应该是一个有$get方法的对象. 可以说provider是一个可配置的factory, 可以存在其他配置对象并通过闭包使用. 在 return 地方可以看到, angular 对所有的 provider 都做了缓存, 所以 provider 都是单例模式的, 包括下面的factory, service, value.

factory

源码:

1
2
3
function factory(name, factoryFn) {
return provider(name, { $get: factoryFn });
}

简单点说就是生成一个 factory 对象, 对象的属性是 factoryFn 里面 return 的任意值.

service

源码:

1
2
3
4
5
6
7
8
function service(name, constructor) {
return factory(name, [
'$injector',
function ($injector) {
return $injector.instantiate(constructor);
},
]);
}

和 factory 基本没区别, 只是通过 new 方式调用的, 你可以在 constructor 函数里面直接对 this 设置值(注意通过 this 设置值的时候不能有 return). 也可以和 factory 一样 return 一个对象.
区别是通过 this 设置值的时候, 会显示原型(在我们项目里面, 只有 service, 大部分也是 return 对象, 原因是…名字比 factory 易懂…).

value

源码:

1
2
3
4
5
6
7
8
function value(name, value) {
return factory(name, valueFn(value));
}
function valueFn(value) {
return function () {
return value;
};
}

很简单, 返回一个常量.

constant

源码:

1
2
3
4
function constant(name, value) {
providerCache[name] = value;
instanceCache[name] = value;
}

和 value 的区别是, 不通过 factory 调用, 即配置完成以后就存在了, 可以在 config 里面注入. 而且是在配置完成的时候立即加载运行的.
通过代码也可以看到constant也是单例模式, 通过缓存读取的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
app.constant('myConstant', '1');
app.config([
'myConstant',
'$provide',
function (myConstant, $provider) {
console.log(myConstant);
},
]);
//有效
app.value('myValue', '1');
app.config([
'myValue',
'$provide',
function (myValue, $provider) {
console.log(myValue);
},
]);
//报错
app.value('myValue', '1');
app.config([
'myValueProvider',
'$provide',
function (myValueProvider, $provider) {
console.log(myValueProvider);
},
]);
//有效, 获取的是provider对象, 而不是$get的值, 所以在部分情况下定义provider的属性可以在config里面获取得到.

decorator

装饰器模式的实现. 可以对 provider 对象进行装饰.

源码:

1
2
3
4
5
6
7
8
function decorator(serviceName, decorFn) {
var origProvider = providerInjector.get(serviceName + providerSuffix),
orig$get = origProvider.$get;
origProvider.$get = function () {
var origInstance = instanceInjector.invoke(orig$get, origProvider);
return instanceInjector.invoke(decorFn, null, { $delegate: origInstance });
};
}

简单点说就是把上面通过provider创建的 provide 的$get 方法替换成新的decorFn, 可以达到运行时修改 service 的功能. 实际使用的情况并不多见. 不能对constant定义的值使用, 因为constant不是通过provider创建的, 但能对value 使用.

多次使用decorator

对于某个provider来说, 使用过decorator以后, 这个provider$get方法已经变成decorator里面替换的$get了, 所以再次使用decorator的时候, 等于对改变过的$get使用decorator, 即会造成叠加而不是替换的效果. 这点和provider重复声明会替换不同.

$injector

angular.injector()可以获得注入器, 如果有引入 jquery, 则$.fn.injector()也可以使用. $injector.has('serviceName')可以判断是否存在这个服务. $injector.get('serviceName')可以通过服务名获得服务对象. $injector.annotate(ctrls)可以获得 ctrls 的依赖对象.

作者

Mosby

发布于

2016-08-18

更新于

2016-08-22

许可协议

评论