zoukankan      html  css  js  c++  java
  • AngularJS模块具体解释

    模块是提供一些特殊服务的功能块。比方本地化模块负责文字本地化,验证模块负责数据验证。一般来说,服务在模块内部,当我们须要某个服务的时候,是先把模块实例化。然后再调用模块的方法。

    但Angular模块和我们通常理解的模块不一样。Angular模块仅仅保留服务的声明,服务的实例化是由服务注入器完毕的,实例化之后服务就留在了服务注入器中,和模块没有关系了,这就是为什么我们使用的服务全部来自注入器的原因。

    每调用一次angular.boostrap()方法会创建一个新的Angular应用和一个新的服务注入器,因此。每一个应用都相应一个服务注入器。彼此互不冲突。

    在angular中,模块能够是对象、方法(假设是数组,数组的最后一个元素必须是方法。前面的元素都是方法按顺序排列的參数名称)。后面讲的模块属性和方法。都属于通过angular.module()定义的模块对象。假设模块是方法。是不须要经过angular.module()定义的,仅仅需写入依赖数组(就是说依赖数组的元素能够是方法)。模块在载入依赖关系的时候直接运行了。

    注意:通过angular.module()方法定义的模块是唯一的。假设反复定义就会覆盖前面的定义。

    angular模块

    angular模块通过angular.module(name,requires, configFn)方法生成:

    • 參数name是模块名称。
    • 參数requires表示依赖模块数组。

      假设不设置requires參数,调用angular.module(name)方法表示获取这个模块;因此,假设确定新模块没有依赖关系,必须设置requires为空数组[];

    • 參数configFn是方法或数组。负责在模块初始化时做一些配置。假设是数组。最后一个元素必须是方法。

    方法configFn并非在运行angular.module()的时候马上运行,而是当这个模块被第一次使用时,由注入器调用运行。

    同一时候,查看方法configFn中的this就会发现,这个this在浏览器中指向的是window。而不是module。并且,方法configFn仅仅会运行一次,因此同一个angular模块不会反复配置。

    參数requires中的字符串表示依赖的模块名称。假设不是字符串。则必须是方法(或数组格式的方法),那么。这种方法就代表了一个模块。

    同名模块

    已经初始化的angular模块保存在一个叫modules的缓存对象中,key是模块名,value是模块对象。所以。定义一个同名的模块,等于覆盖之前的模块。

    服务注入

    前面已经讲了,angular模块仅仅保留服务的定义,如今我们再来理解服务是怎样加入注入器的。

    在了解服务注入器前。还须要了解还有一个概念,就是服务提供商。在Angular中称为Provider,差点儿全部的服务(除了$injector)都是由服务提供商供应。不管是服务还是服务提供商。他们在Angular中都是唯一的,服务和服务提供商是一个一对一的关系。也正是由于这个关系,我们在使用模块service()、value()等方法时,感觉我们定义的好像仅仅是服务,但事实上Angular背后为这些服务都创建了一一相应的服务提供商。注入器的机制就是当我们须要某个服务的时候,首先依据服务名找到相应的服务提供商。然后由服务提供商创建相应的服务并返回。

    所以这就是整个过程:

    1.  模块定义服务、服务提供商。
    2.  注入器依据模块依赖关系载入模块,实例化全部服务提供商;
    3.  应用须要服务,注入器依据服务名寻找服务提供商。服务提供商实例化服务。

    以上仅仅是理论。如今从代码层面来看Angular是怎样实现的。

    每一个angular模块内置有三个数组。invokeQueue保存怎样注入服务提供商和值的信息;configBlocks保存模块的配置信息。runBlocks保存这个模块的运行信息。

    模块被使用的时候,注入器依据invokeQueue中的信息。实例化服务提供商;依据configBlocks中的信息对服务提供商做一些额外的处理;依据runBlocks中提供的信息,调用前面的服务提供商提供的服务运行模块须要完毕的工作。

    Angular模块

    angular模块提供了非常多方法来填充这三个数组,比方config()、run()等。三个数组的元素也是数组。详细元素格式參考后面的说明。

    模块方法

    调用队列 – invokeQueue

    数组元素为[‘provider’, ‘method’, arguments]。

    举例– 加入一个Controller:

    angular.module('ngAppDemo',[])
    
    .controller('ngAppDemoController',function($scope) {
    
          $scope.a= 1;
    
          $scope.b = 2;
    
    });
    

    这段代码等于:

    invokeQueue.push(['$controllerProvider','register', ['ngAppDemoController', function(){}]]);

    注入器依据这个信息,就会调用$controllerProvider的register方法注冊一个ngAppDemoController。

    配置队列 – configBlocks

    元素格式为['$injector', 'invoke', arguments]。

    运行队列 – runBlocks

    元素要求是方法,或者是数组。数组最后一个元素是方法。

    angular模块实例属性和方法

    属性

    requires

    表示模块的依赖数组。即angular.module方法的requires參数。

    name

    模块的名字。

    _invokeQueue、_configBlocks、_runBlocks

    分别相应invokeQueue、configBlocks、runBlocks。

    方法

    模块的下面方法最后全部会返回模块实例本身,形成运行链

    animation()

    调用这种方法表示这个模块将在$animateProvider中注冊一个动画服务。

    原理:invokeQueue尾部插入['$animateProvider', 'register', arguments]。

    config()

    调用这种方法,表示给这个模块追加一个配置方法。

    原理:在configBlocks尾部插入['$injector','invoke', arguments]

    constant()

    调用这种方法表示这个模块将给默认的$provider注冊一个常量。

    原理:在invokeQueue首部插入['$provide', 'constant', arguments]。

    controller()

    调用这种方法表示模块将在$controllerProvider中注冊一个控制器。

    原理:在invokeQueue尾部插入['$controllerProvider', 'register', arguments]。

    directive()

    调用这种方法表示这个模块将在$compileProvider中注冊一个指令。

    原理:在invokeQueue尾部插入['$compileProvider', 'directive', arguments]。

    factory()

    调用这种方法表示这个模块中将生成一个服务工厂(隐式创建一个了服务提供商)。

    原理:在invokeQueue尾部插入['$provide', 'factory', arguments]。

    filter()

    调用这种方法表示这个模块将在$filterProvider中注冊一个过滤器。

    原理:在invokeQueue尾部插入['$filterProvider', 'register', arguments]。

    provider()

    调用这种方法表示这个模块将加入一个服务提供商。

    原理:在invokeQueue尾部插入['$provide', 'provider', arguments]。

    run(block)

    调用这种方法表示这个模块将运行某个功能块。block能够是方法,也能够是数组。

    原理:在invokeQueue尾部插入block。

    service()

    调用这种方法表示这个模块将注冊一个服务(隐式创建了一个服务提供商)。

    原理:在invokeQueue尾部插入['$provide', 'service', arguments]。

    value()

    调用这种方法表示这个模块将注冊一个变量(隐式创建了一个服务提供商)。

    原理:在invokeQueue尾部插入['$provide', 'value', arguments]。

    服务注入器(Service Injector) & 服务提供商(Service Provider)

    在Angular中,服务可能是对象、方法、或者一个常量值。服务由服务提供商创建。而服务提供商由注入器统一管理。当我们须要某个服务的时候。注入器负责依据服务名寻找相应的服务提供商,然后由服务提供商的$get()生产工厂创建服务实例。因此。服务提供商必须有一个$get()方法,这种方法就是服务创建单例工厂。

    背后原理:注入器中的Providers和Services各自通过一个Map对象保存在缓存(分别相应providerCache和instanceCache)中,仅仅只是Providers的key是serviceName + “Provider”,而Services的key是serviceName。

    服务提供商-Provider

    Provider即服务提供商,必须有一个$get()方法。$get()的返回值是Provider在注入器中实际的服务。

    注入器

    创建注入器的方法仅仅在bootstrap()方法中被调用过,也就是说,每一个angular应用相应一个注入器。

    注入过程

    注入器由angular.injector(modulesToLoad, isStrictDi)方法创建。在angular中事实上为createInjector方法。參数modulesToLoad是数组,元素格式为下面之中的一个:

    • ‘module’,模块的名称。
    • [‘service1’, ‘service2’, fn]。
    • fn,方法的返回值必须仍然是方法。

    方法angular.injector()的运行过程:

    1.        遍历modulesToLoad。依据moduleName寻找或生成相应的模块。

    2.        调用模块invokeQueue中的全部方法。这一步的目的是创建必需的服务。

    3.        调用模块configBlocks中的全部方法,目的是用已有的服务配置这个模块。

    4.        调用注入器的invoke()方法运行模块runBlocks的全部方法。

    5.        返回服务注入器实例。

    不是全部模块都是对象,假设模块本身是方法或者是数组(最后一个元素必须是方法)。则运行这种方法、或数组的最后一个方法,相当于直接进入了第四步。

    注入器与bootstrap的关系

    创建注入器的方法在angular.js中仅仅在bootstrap()方法中被调用过。也就是说,每一个angular应用相应一个注入器。

    注入器方法

    在providerCache中和instanceCache中分别内置有一个$injector对象,分别负责给模块注入服务提供商和为方法注入服务。

    一般我们仅仅谈论instanceCache中的$injector对象。由于providerCache和它的$injector是私有的,仅仅在Angular内部代码使用。

    比方,运行模块调用队列、配置队列中的方法时注入的是服务提供商,而当调用运行队列中的方法时,注入的是服务。

    $injector.invoke(fn, self, locals, serviceName)

    运行方法fn。

    locals是可选參数,是对象,表示局部变量。

    self是fn中的this。

    最后一个參数serviceName是可选參数,表示在哪个服务中调用了fn方法,用于错误信息显示,没有处理逻辑。

    $injector.instantiate(Type, locals, serviceName)

    l  假设參数Type为方法,依据Type的prototype创建一个实例(通过Object.create方法创建),假设Type是数组。使用最后一个元素的prototype。

    l  參数Locals是当Type方法的參数出如今locals对象中的时候。取locals[arg]的值又一次作为Type的參数。假设locals中没有。则等价于调用get(arg,serviceName)获取service作为新的參数。

    实例化过程能够简单概括为

    Type.apply(Object.create(Type.prototype),locals[argName]|| get(argName, serviceName))。

    注意:实例化出来的不一定是对象。也可能是方法。

    最后一个參数serviceName是可选參数,表示在哪个服务中实例化了Type,用于错误信息显示,没有处理逻辑。

    $injector.get(name, caller)

    从注入器中获取一个服务实例。

    參数name是服务的名称。參数caller也是字符串,表示调用这个服务的是哪个方法,用于错误信息提示。没有处理逻辑。

    $injector.annotate(fn[,strictDi][,name] )

    返回数组。数组的元素是fn方法须要注入的依赖服务。

    在严格模式下,方法的依赖注入必须使用显示的注解加入,也就是说通过fn.$injector能够获取这种方法的依赖注入。

    參数name是可选的,用于错误显示,没有处理逻辑。

    方法annotate()也能够接受数组,数组的最后一个參数一定是fn,前面的元素则是依赖。

    $injector.has(name)

    检查该注入器中是否存在指定的服务。

    Provider方法

    注入器的providerCache中内置有一个$provider对象,这是注入器的默认服务提供商,$provider有六个固定的方法。这几个方法的作用主要是为注入器加入其他服务提供商。

    注意:

    • 下面全部方法的name參数不须要以“Provider”结尾,由于provider()方法会默认把这个后缀加上。

    • 下面不论什么一个方法不做同名推断,因此,假设出现同名,后者将覆盖前者。

    $provider.provide(name, provider)

    參数provider能够是方法或数组,也能够是对象。

    l  假设是方法,则是provider的构造函数。

    调用注入器的instantiate()方法,生成一个provider实例。并以name为key保存在注入器的providerCache中。

    l  假设是数组。最后一个必须是provider的构造函数。前面的就是构造函数的參数名。之后的原理和provider是方法的情形同样。

    l  假设是对象,说明这个provider已经被实例化了,仅仅需有$get()方法就可以。

    $provider.factory(name, factoryFn, enforce)

    使用$provider.provide()一般须要定义一个Provider类,假设不想定义Provider类,而是直接定义服务工厂。就能够使用这种方法。

    背后原理:首先生成一个匿名对象,这个对象的$get属性就是factoryFn(enforce为false的情况下),然后把这个匿名对象作为$provider.provide()方法的第二个參数。所以。factoryFn事实上依旧是绑定在一个provider上的。

    $provider.service(name, constructor)

    调用injector.instantiate()方法,利用參数constructor生成service实例,參数name是这个service的名称。

    众所周知,service由provider提供,那这种方法是怎么回事?原理:Angular首先依据constructor生成一个factoryFn,然后调用$provider.factory(name, factoryFn)。

    所以事实上还是生成了一个provider。

    举例:

    $provider.service('filter', constructor)

    等于创建了一个filter服务实例。并且在providerCache中保存了一个名称为“filterProvider”的服务提供商。

          

    $provider.value(name, value)

    这种方法实际调用injector.factory(name,valueFn(value), false)方法实现。所以事实上等于创建一个仅仅提供值的服务提供商。

    $provider.constant(name, value)

    这种方法直接在providerCache中加入一个属性实现。

    $provider.decorate(serviceName, decorFn)

    改动旧的服务,改为运行decorFn方法,并把servcieName原来的服务作为一个參数。參数名为$delegate。等价于一个静态代理。

    背后原理:首先依据seviceName找到Provider,然后改动provider的$get属性。

    服务实例化

    全部服务都由Provider管理,除了常量值,其他一般都是还没有创建出来的。

    了解了注入器的全部方法。也应该能够看出,仅仅有两个方法:get()和invoke()真实的调用了服务。

    事实上,服务的实例化实际是在注入器的get()方法中完毕的。而invoke()仅仅是在须要的时候调用了get()。

    angular内置模块

    ngLocale - 本地化模块

    angular.module('ngLocale',[]).provider('$locale', $LocaleProvider);

    结果是invokeQueue.push(['$provide', 'provider',['$locale', $LocaleProvider]]);

    ng

    angular.module('ng',['ngLocale']).config(['$provide', function(){}]);

    结果是configBlocks.push(['$injector', 'invoke',['$provide', function(){}]])。

    三个固定模块

    每一个使用bootstrap(element, modules, config)生成的应用,注入器中有三个固定的模块:

    • 第一个模块是"ng"。
    • 第二个模块是[‘$provider’,fn],它的作用是把根元素element作为变量保存在$provider中。

    • 第三个模块是[‘$compileProvider’,fn],它的作用是依据config.debugInfoEnabled调用 $conpileProvider.debugInfoEnabled(true)。
  • 相关阅读:
    Sqlserver 实际开发中表变量的用法
    Python Day 20 面向对象 (面向对象的组合用法,面向对象的三大特性
    Python Day 19 面向对象(初识面向对象)
    Python Day 18 常用模块(模块和包)
    Python Day 17 常用模块(常用模块一 时间模块,random模块,os模块,sys模块,序列化模块)
    Python Day 15 函数(递归函数、二分查找算法)
    Python Day 14 函数(内置函数,匿名函数(lambda表达式))
    Python Day 13 函数(迭代器,生成器,列表推导式,生成器表达式)
    Python Day 11 + Python Day 12 函数(函数名的应用,闭包,装饰器)
    Python Day 10 函数(名称空间,作用域,作用域链,加载顺序等; 函数的嵌套 global,nonlocal)
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7284270.html
Copyright © 2011-2022 走看看