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。