angular源码分析1-模块化管理

version:1.3.0

angular初始化

angular.js加载后,首先初始化。初始化包括以下3个部分:

(1)bindJQuey()方法绑定jquery。如果之前加载有jquey,则angulae.element与jquery绑定,否则,angular.element使用angular自己实现的jqlite。

(2)publishExternalAPI(angular)给angular添加属性方法,属性方法暴漏出来,创建模块加载器。

(3)jqLite(document).ready(function(){angularInit(document,bootstarp)});找到angular作用的范围的入口ng-app节点,初始化ng-app关联的模块及其子模块,然后编译链接。

angular模块化管理

1.设置模块加载器、模块注册

即生成angular.module()函数,用于注册模块、获取模块、给模块注册自己的service、controller 、factory、direactive等。通过setupModuleLoader(window)函数把一个函数返回给angularModule,并且返回的的函数绑定在window.angular.module上。

(1)setupModuleLoader函数实现

第1719行给angular添加到window对象上,并赋值为空对象。即window.angular = {}。

第1720到1766行返回一个一个闭包函数function(name,requires,configFn)并且把这个函数赋值给window.angular.module。第1726到1764行给modules对像添加一个模块name属性,即注册的模块存储到modules对象。modules.name赋值为一个包含_invokeQueue、provider、factory等属性的moduleInstance对象,这个对象并且返回。moduleInstance对象属性含义:_invokeQueue队列存储该模块注册的provider、factory、service、value、constant、animation、filter、controller、directive。_configBlocks队列存储该模块注册的config。_runBlocks队列存储该模块注册的run。第1757行到1763行的工具函数invokeLater使用了闭包,返回了一个函数,用于把注册的信息放入队列。

(2)执行过程

通过setupModuleLoader函数返回module函数并且赋值给window.angular.module。通过angular.module函数可以注册模块、获取模块以及给每个模块这册provider、factory、service、value、constant、animation、filter、controller、directive。

调用函数angular.module('app',[])注册app模块,modules.app = moduleInstance对象,并且返回这个对象。第4到第6行调用该对象的run方法,把function(){console.log('app run');})添加到moduleInstance对象的_runBlocks队列中,并返回该moduleInstance对象。第7到第9行调用该对象的config方法,把['$injector','invoke','function(){console.log('app config');})放入到moduleInstance对象的_configBlocks队列中。

第40行调用angular.module('app')获取modles.app对象,并且返回modules.app对象。第41到第43行,调用该对象的service方法,把['$provide','service',['baseService',[function(){this.base = 'base';}]]放入该对象的_invokeQueue队列中,并返回该对象。第44到第46行,调用该对象的controller方法,把['$controllerProvider','register',['testController',['$scope',function(scope){}]]]放入该对象的_invokeQueue队列中,并返回该对象。

2.加载依赖的模块及其子模块

angular使用jqlite(document).ready(function(){})方法在html多有资源加载完后,找到ng-app元素节点,ng-app绑定的模块及其子模块,执行模块的_invokeQueue队列中的config方法、_configBlocks队列的run方法,加工_invokeQueue队列中的元素。

html模版:

app模块及其依赖模块注册:

通过以上代码模块的注册及模块的config、run、service、factory等的注册,产生的modules对象的数据包含有以下数据:

modules = {
    app:{
       animation:function(){}
       config:function(){}
       constant:function(){}
       controller:function(){}
       directive:function(){}
       factory:function(){}
       filter:function(){}
       name:'app",
       provider:function(){}
       requires:['app1','app2']
       run:function(block){}
       service:function(){}
       value:function(){}
      //Arguments为传的参数值,此处包含一个参数,即app模块传入config    
      方法的函数function(){console.log('app config')}
       _configBlocks:[['$injector','invoke',Arguments(1)]]
      //第一个Arguments(2)是调用app模块service传的实参
      //第二个Arguments(2)是调用app模块的controller传的的实参
       _invokeQueue:[['$provide','service',Arguments(2)],
       ['$controllerProvider','register',Arguments(2)]]
       _runBlocks:[function(){console.log('app run')}]
    }
    app1:{
       ...
       name: 'app1'
       requires:['app11']
       //Arguments(1)为app11模块调用config方法传的实参
       _configBlocks:[['$injector','invoke',Arguments(1)]]
       _invokeQueue:[]
       _runBlocks:[function(){console.log('app11 run')}]
    }
    app2:{
       ...
    }
    app11:{
       ...
    }
    ng:{...}
    ngLocale:{...}
}    

angular在启动时,即调用angular.bootstrap时,使用loadModules方法加载依赖的模块。调用下面一条语句作为加载模块的入口,modulesToLoad的值为['ng',['$provide',function(){$provide.value('$rootElement', element);}],'app']。下面只分析'app'及其依赖模块的加载过程。

模块之间的关系如上图的树状结构所示,app模块依赖app1模块和app2模块,app1模块依赖app11模块。

下面是加载app及其依赖模块代码:

 

通过第3730行代码遍历到'app'模块时,作为遍历'app'模块及其依赖的子模块的入口,调用loadModules('app')开始加载app模块及其依赖的子模块。在加载app模块及子模块时使用了递归,第3821行到3824行完成了模块的递归加载,先加载子模块,后加载父模块,遍历的路径:app11 -> app1 -> app2 ->app。第3822行,在递归loadModules时,把app11、app1、app2、app各模块对象的_runBlocks依次连接组合成一个新的数组。第3823行,依次执行app11、app1、app2、app各模块对象_invokeQueue队列,对每个队列做进一步处理,如何处理,下一步分析。第3824行,依次执行app11、app1、app2、app各模块对象_configBlocks队列中的函数,即调用模块config注册的方法。递归执行完loadModules('app')后,返回_各模块_runBlocks拼接组合的新的数组,然后在第3730行一次调用该数组的方法,即各模块通过run方法注册的函数。

第3823行把模块的_invokeQueue进一步处理并且存储到providerCache对象中,以备在以后的编译过程中使用。

以上代码给app模块注册controller、service、provider、actory、provider、directive、filter把相关信息存储到模块的对象的_invokeQueue队列中。

在加载模块时,通过第3823行代码把_invokeQueue队列成员依次处理,并且分别存储在providerCache对象中,以备编译时使用。

providerCache.testServiceProvider = object;

providerCache.testProviderProvider = object;

...

上面的转换过程通过provideCache.$provide的函数来处理。

在上面的处理过程中使用到了$provide,这也是angular的核心。下节分析angular的两个核心部分$provide和$injector。

posted @ 2017-06-11 17:40  springmin  阅读(602)  评论(0编辑  收藏  举报