zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    vm8.0安装mac提示中断导致虚拟cpu进入终止状态……vm重启的解决办法
    WPF和WindowsForm下的按下Enter跳转下一个控件通用方法
    【Z】oledb读写excel出现“操作必须使用一个可更新的查询”错误
    关于系统编码的那点事
    【Z】使用OleDbCommandBuilder时出现“Insert into 语句的语法错误”
    Thread 初学(二)——线程同步
    Oracle中针对一张表建立触发器,并且触发器也处理同一张表的数据(ora04091 错误)
    Thread初学 (一)
    【Z】使用SQL Server的OPENROWSET函数
    13个绚丽的Jquery 界面设计
  • 原文地址:https://www.cnblogs.com/fe-huahai/p/6985749.html
Copyright © 2011-2022 走看看