天天看点

Angular-依赖注入

原文: 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 。
  • run

     方法接受一个可以依赖注入 "service", "value" 和"constant" 组件的函数。注意你不能在

    run

     块中注入 "providers" 。
  • config

     方法接受一个可以依赖注入 "provider" 和"constant" 组件的函数。注意你不能在 configuration中注入"service" 或 "value" 组件。
参考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

 和

run

 方法运行。这些函数可以被带依赖注入就像上面的 factory 函数。
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也可用:

    • $scope

      : Controllers跟DOM的元素有关,因而可以访问 scope。 其他组件 (例如services) 只能访问 

      $rootScope

       service。
    • 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需要跟 

$inject

 property一同注解。 

$inject

 property释一个要注入的 service 名字数组。
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)、使用严格的依赖注入

你可以在与 

ng-app

 同样的element添加 

ng-strict-di

 directive , 来打开严格的 DI 模式:
<!doctype html>
           
I can add: {{ 1 + 2 }}.
           

严格模式跑出错误每当 service 尝试使用 隐形注解。

参考这个module,包含 使用隐形DI的 

willBreak

 service:
angular.module('myApp', [])
           
})
           
}]);
           
当 

willBreak

 service初始化,Angular 会因为严格模式抛出错误。当使用类似 ng-annotate 工具来确保所有应用组件带有注解。

若正在使用人工引导,你能够在选项配置参数中提供 

strictDi: true

 来使用隐形DI:
strictDi: true
           
});
           

3)、为何依赖注入?

这段深入解释Angular的DI使用。关于怎么用请看上文。

为深入探讨 DI,参考维基百科Dependency Injection ,Martin Fowle写的 Inversion of Control ,或者在你的软件设计模式书中阅读 DI 。

只有三种方式一个 component (object or function) 能掌握它的依赖关系:

    1. component 能够创建依赖,通常使用 

      new

       操作。
    2. component 能够查询依赖,通过引用一个全局变量。
    3. component 能够在它需要的地方有依赖传过来。

前两个方法创建或查找依赖不是最优选,因为它们在component中硬编码。为了改变依赖关系会带来困惑。若不是不可能, 要修改依赖关系。尤其测试中更有问题,在那它经常为了测试隔离需要提供模拟关系。

第三个方法最可行,因为它去掉了从component定位依赖关系的义务。依赖关系仅仅传递给component。

this.greeter = greeter;
           
}
           
this.greeter.greet(name);
           
}
           
上面例子 

SomeClass

 不要关心创建或定位 

greeter

 依赖,仅仅

greeter初始化的时候传递。

这个示意图, 但它把掌控依赖的责任推给构造

SomeClass

.的代码。

为了管理dependency 创建的责任,每个 Angular 应用都有一个 injector。 injector 是一个 service locator 负责构造并查询依赖关系。

这个例子使用 injector service:

var myModule = angular.module('myModule', []);
           
教 injector 怎么构建 

greeter

 service。注意 

greeter

 依赖 

$window

 service。 

greeter

 service 是一个包含 

greet

 method的对象。
$window.alert(text);
           
}
           
};
           
});
           
创建一个新的injector 能提供定义在 

myModule

 module的components 并从injector申请 

greeter

 service。(通常由 angular bootstrap自动完成)。
var injector = angular.injector(['ng', 'myModule']);
           
var greeter = injector.get('greeter');
           
要求依赖关系,解决了硬编码问题,但它也意味着 injector 需要始终传到应用。传递 injector 破坏了 Law of Demeter。为了补救,在HTML templates中使用declarative 符号,来担当越过injector创建的责任,例如此例:
greeter.greet('Hello World');
           
};
           
}
           
当Angular编译 HTML,它处理 

ng-controller

 directive,反过来要求 injector创建  controller 和它的依赖关系的对象。
injector.instantiate(MyController);
           
所有都在幕后完成,注意拥有 

ng-controller

 要求njector初始化 class,它可以满足 

MyController

 所有的i依赖关系,而不需要controller熟悉injector。

最佳结果,应用代码code 仅仅申明它的依赖关系,而不用跟 injector交涉。这种设置不会破坏 Law of Demeter。

Note: Angular 使用  constructor injection.

继续阅读