原文: https://code.angularjs.org/1.5.0-rc.0/docs/guide/di
依赖注入(DI)是软件设计模式,用来处理组件怎样掌控它们的依赖关系。 Angular注入子系统负责创建组件,解决它们的依赖关系,并把它们提供给其它需要的组件。
1)、使用依赖注入
依赖注入贯穿于Angular,可以在定义组件或给模提供和
run
代码块时候使用它。
config
-
比如 services, directives, filters, and animations组件是通过可注入的Factory方法或构造函数定义。这些组件可以依赖注入一些 "service" 和"value" 组件。
-
Controllers通过构造函数定义, 可以依赖注 "service" 和"value"组件,但它们也就可以作为特殊依赖关系提供方。参考 下面的一系列此类特殊依赖关系的 Controllers 。
-
方法接受一个可以依赖注入 "service", "value" 和"constant" 组件的函数。注意你不能在run
块中注入 "providers" 。run
-
方法接受一个可以依赖注入 "provider" 和"constant" 组件的函数。注意你不能在 configuration中注入"service" 或 "value" 组件。config
参考Modules 获取详细的关于和
run
代码块的信息。
config
1.1)、Factory 方法
跟定义 directive, service, 或 filter的方式一样定义factory 函数。factory 方法也带module注册。申明factories推荐的方式是:
angular.module('myModule', []) .factory('serviceId', ['depService', function(depService) { // ... }]) .directive('directiveName', ['depService', function(depService) { // ... }]) .filter('filterName', ['depService', function(depService) { // ... }]);
1.2)、Module 方法
可以为module指定函数在configuration时或run time时通过调用和
config
方法运行。这些函数可以被带依赖注入就像上面的 factory 函数。
run
angular.module('myModule', []) .config(['depProvider', function(depProvider) { // ... }]) .run(['depService', function(depService) { // ... }]);
1.3)、Controllers
Controllers 是"classes" 或"constructor functions" 负责提供应用行为,可以支持在template中申明标记。申明 Controllers推荐的方式是使用数组表示:someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) { ... $scope.aMethod = function() { ... } ... }]);
不像services,在应用中可以有同类型controller的很多对象。
还有,额外的依赖对 Controllers也可用:
-
-
: Controllers跟DOM的元素有关,因而可以访问 scope。 其他组件 (例如services) 只能访问$scope
service。$rootScope
- resolves: 若controller初始化为route的一部分,那任何被解析为route的一部分的值能注入到controller。
-
这里数组中的值的顺序必须匹配
$inject
MyController
参数的顺序。
就像数组注解,你需要注意保持
跟函数申明的参数同步。
$inject
2)、依赖注解
Angular 通过注入器调用某些函数 (例如service factories 和 controllers)。你需要注解这些functions来让注入器知道该个妞function注入什么services。有三种方法来使用service名字信息来注解你的代码:
-
- 使用内联数组注解(优选)
- 使用
属性注解$inject
- 从函数参数名隐形注解(有注意事项)
2.1)、内联数组注解
这个是优选方法来注解应用组件。这是文档中例子写法。
例如:
someModule.controller('MyController', ['$scope', 'greeter', function($scope, greeter) { // ... }]);
这里传递一个数组,它的元素有字符串列表组成 (依赖关系名字)跟在function 后面。
当使用这种类型注解,注意保持注解数组与函数声明的参数同步。
2.2)、$inject 属性注解
为了允许minifiers重命名function parameters并仍可以注入正确的services, function需要跟property一同注解。
$inject
property释一个要注入的 service 名字数组。
$inject
这种情况下,var MyController = function($scope, greeter) { // ... } MyController.$inject = ['$scope', 'greeter']; someModule.controller('MyController', MyController);
数组中值得顺序必须匹配
$inject
MyController
.中参数的顺序。
就像数组注解,你需要保持
与函数声明的参数同步。
$inject
2.3)、隐式注解
注意: 若打算 minify 代码,service 名字会被重命名并打算你的app。
最简单的掌控依赖关系的方法是设想函数参数即为所依赖的名字。
给定一个函数,注入器可以通过测验函数的申明和检查参数名字来推断要注入的services名字。在上述例子,
和
$scope
greeter
是两个需要被注入到函数的services 。
这个方法的一个优点是这没有任何名字数组与函数参数保持同步。你也可以自由排序依赖关系。
然而这个方法不会与JavaScript minifiers/obfuscators一起工作,因为他们重命名参数的方式。
像 ng-annotate 工具让你在应用中使用隐形依赖注解并在minifying之前自动添加内联数组注解。若你决定利用这个方法,你可以使用
ng-strict-di
。
因为这些注意事项。建议避免使用这个注解风格。
3)、使用严格的依赖注入
你可以在与同样的element添加
ng-app
directive , 来打开严格的 DI 模式:
ng-strict-di
<!doctype html>
I can add: {{ 1 + 2 }}.
严格模式跑出错误每当 service 尝试使用 隐形注解。
参考这个module,包含 使用隐形DI的
service:
willBreak
当
angular.module('myApp', [])
})
}]);
willBreak
service初始化,Angular 会因为严格模式抛出错误。当使用类似 ng-annotate 工具来确保所有应用组件带有注解。
若正在使用人工引导,你能够在选项配置参数中提供
来使用隐形DI:
strictDi: true
strictDi: true
});
3)、为何依赖注入?
这段深入解释Angular的DI使用。关于怎么用请看上文。
为深入探讨 DI,参考维基百科Dependency Injection ,Martin Fowle写的 Inversion of Control ,或者在你的软件设计模式书中阅读 DI 。
只有三种方式一个 component (object or function) 能掌握它的依赖关系:
-
- component 能够创建依赖,通常使用
操作。new
- component 能够查询依赖,通过引用一个全局变量。
- component 能够在它需要的地方有依赖传过来。
- component 能够创建依赖,通常使用
前两个方法创建或查找依赖不是最优选,因为它们在component中硬编码。为了改变依赖关系会带来困惑。若不是不可能, 要修改依赖关系。尤其测试中更有问题,在那它经常为了测试隔离需要提供模拟关系。
第三个方法最可行,因为它去掉了从component定位依赖关系的义务。依赖关系仅仅传递给component。
上面例子
this.greeter = greeter;
}
this.greeter.greet(name);
}
不要关心创建或定位
SomeClass
依赖,仅仅
greeter
这个示意图, 但它把掌控依赖的责任推给构造
greeter初始化的时候传递。
.的代码。
SomeClass
为了管理dependency 创建的责任,每个 Angular 应用都有一个 injector。 injector 是一个 service locator 负责构造并查询依赖关系。
这个例子使用 injector service:
教 injector 怎么构建
var myModule = angular.module('myModule', []);
service。注意
greeter
依赖
greeter
service。
$window
service 是一个包含
greeter
method的对象。
greet
创建一个新的injector 能提供定义在
$window.alert(text);
}
};
});
module的components 并从injector申请
myModule
service。(通常由 angular bootstrap自动完成)。
greeter
要求依赖关系,解决了硬编码问题,但它也意味着 injector 需要始终传到应用。传递 injector 破坏了 Law of Demeter。为了补救,在HTML templates中使用declarative 符号,来担当越过injector创建的责任,例如此例:
var injector = angular.injector(['ng', 'myModule']);
var greeter = injector.get('greeter');
当Angular编译 HTML,它处理
greeter.greet('Hello World');
};
}
directive,反过来要求 injector创建 controller 和它的依赖关系的对象。
ng-controller
所有都在幕后完成,注意拥有injector.instantiate(MyController);
要求njector初始化 class,它可以满足
ng-controller
MyController
所有的i依赖关系,而不需要controller熟悉injector。
最佳结果,应用代码code 仅仅申明它的依赖关系,而不用跟 injector交涉。这种设置不会破坏 Law of Demeter。
Note: Angular 使用 constructor injection.