博弈AngularJS讲义(14) - 模块
模块
我们可以把angular中的模块(module)想象成应用组件的容器,这些组件包括控制器,服务,过滤器,指令等.
为什么?
有过编程经验的都知道大部分应用都有一个main方法来初始化应用组件并把它们关联起来. Angular不提供这样的主函数,而是通过声明式的语法定义如何启动一个应用. 这种声明式的方法有如下几个优点:
- 声明式的语言易于理解
- 可以将代码封装成可重用的模块,解耦
- 模块可以以任意顺序启动(甚至可以并行),因为模块是延迟执行的
- 易于测试,测试时只需载入相关模块,可配置性高
基本概念
通过“Hello World”实例来理解模块的工作原理。
index.html
<div ng-app="myApp"> <div> {{ 'World' | greet }} </div> </div>
script.js
// declare a module var myAppModule = angular.module('myApp', []); // configure the module. // in this example we will create a greeting filter myAppModule.filter('greet', function() { return function(name) { return 'Hello, ' + name + '!'; }; });
protractor.js
it('should add Hello to the name', function() { expect(element(by.binding("'World' | greet")).getText()).toEqual('Hello, World!'); });
运行结果:
在这个例子中需注意如下几点:
- 在js中我们通过Module API中的module定义一个模块,并在其中添加greet过滤器。
- module方法的第二个参数为一个数组,用来声明myApp模块所依赖的其他模块,这里angular.module('myApp', [])的第二个参数为空数组,及myApp不依赖任何模块。
- 在HTML模板中通过<div ng-app="myApp">来启动myApp.
上面的例子很简单,在构造复杂的应用时我们要按照下面的方法将应用切割成相对独立的多个模块:
- 按feature划分模块
- 把可重用的组件,特别是指令,过滤器之类的封装到模块中
- 应用级别的模块依赖上面两类模块,并且包含了应用的初始化代码
可以参考Google官方文档: http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html
例如:
angular.module('xmpl.service', []) .value('greeter', { salutation: 'Hello', localize: function(localization) { this.salutation = localization.salutation; }, greet: function(name) { return this.salutation + ' ' + name + '!'; } }) .value('user', { load: function(name) { this.name = name; } }); angular.module('xmpl.directive', []); angular.module('xmpl.filter', []); angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter']) .run(function(greeter, user) { // This is effectively part of the main method initialization code greeter.localize({ salutation: 'Bonjour' }); user.load('World'); }) .controller('XmplController', function($scope, greeter, user){ $scope.greeting = greeter.greet(user.name); });
<div ng-controller="XmplController"> {{ greeting }} </div>
模块的载入和依赖
模块中包含了配置项和运行代码:
- 配置项代码块(Configuration Blocks):将在组件注册或者模块配置阶段运行。配置代码块中只能注入组件的provider和常量值,之所以不运行注入服务是为了防止该类组件没有被初始化完毕即被误用。
- 运行代码块(Run Blocks): 这类代码块会在注入器初始化完成后来启动应用。只有组件实例(not providers)和常量可以被注入, 如果运行时配置项发生改变将被忽略。
示例代码:
angular.module('myModule', []). config(function(injectables) { // provider-injector // This is an example of config block. // You can have as many of these as you want. // You can only inject Providers (not instances) // into config blocks. }). run(function(injectables) { // instance-injector // This is an example of a run block. // You can have as many of these as you want. // You can only inject instances (not Providers) // into run blocks });
模块配置项
可以通过如下代码配置模块:
angular.module('myModule', []). value('a', 123). factory('a', function() { return 123; }). directive('directiveName', ...). filter('filterName', ...); // is same as angular.module('myModule', []). config(function($provide, $compileProvider, $filterProvider) { $provide.value('a', 123); $provide.factory('a', function() { return 123; }); $compileProvider.directive('directiveName', ...); $filterProvider.register('filterName', ...); });
angular启动应用时会先初始化常量定义,随后会执行配置项代码块,依次注册组件。
运行代码块(Run Blocks)
运行代码块的作用类似于main方法,用来启动应用。这些代码块会在所有服务组件被初始化并注入后运行。Run Block中的代码一般都难以进行单元测试,一般都在独立的模块中进行声明,这样在可以在单元测试时被忽略。
依赖
模块可以依赖其他模块,所依赖的模块会在需要时被注入进来,并且每个模块只被载入一次。
异步载入模块
Angular通过模块来管理注入器的配置,模块的载入与JS文件载入无关。我们可以通过require.js等第三方类库来异步载入js文件以提高效率。
创建并获取模块
在上面的例子中我们用angular.module('myModule', [])创建了myModule模块或者重写myModule如果该模块已经存在。可以通过angular.module('myModule')来获取myNodule模块。
参考如下实例代码:
var myModule = angular.module('myModule', []); // add some directives and services myModule.service('myService', ...); myModule.directive('myDirective', ...); // overwrites both myService and myDirective by creating a new module var myModule = angular.module('myModule', []); // throws an error because myOtherModule has yet to be defined var myModule = angular.module('myOtherModule');
上一篇: Linux命令之用户和群组