一、从源代码的编译顺序开始

下面是我们在目录结构哪一期理出的angular的编辑顺序图的缩略版:

├─── angular.prefix   //util.wrap函数加入的前缀代码  
│  
├─── minerr.js       //错误处理  
├─── Angular.js      //主要定义angular的工具函数 
├─── loader.js       //定义了setupModuleLoader函数  
├─── stringify.js    //定义了对象序列化serializeObject,和对象调试输出字符串serializeObject  
├─── AngularPublic.js //定义了angular导出的函数和变量  
├─── jqLite.js        //定义jqLite,一个mini的jQuery  
├─── apis.js          //定义了关于对象hash值的几个函数  
├─── auto
│     │
│     └─── injector.js //依赖注入和模块加载,主要在这里实现,我在[第二期]讲过部分  
│  
├─── ng                //定义angular的各种服务的目录,该目录下一个文件按名字对应一个服务  
│    │
│    ├─── *.js        //各种服务的定义
│    ├─── filter.js     //定义过滤器,注册具体的过滤器
│    ├─── filter         //过滤器目录, 
│    │     │
│    │     └─── *.js   //过滤器的具体实现
│    └─── directive      //指令目录,该目录下一个文件对应一个angular指令 
│          │ 
│          └─── *.js     //指令的具体实现
├─── angular.bind.js           //简单几行代码,判断是否已经加载了jQuery,如果是,直接使用jQuery,而不使用jqLite  
├─── publishExternalApis.js        // `publishExternalAPI(angular);`导出变量和接口  
│  
└── angular.suffix   //util.wrap函数加入的后缀代码

二、找到代码的入口点

//try to bind to jquery Now so that one can write angular.element().read()
  //but we will rebind on bootstrap again.
  bindJQuery();  //绑定jquery:如果系统已经加载了jQuery,绑定使用,如果没有则是用angular自身的jqLite

  publishExternalAPI(angular); //导出angular的对外公开的函数和属性

  jqLite(document).ready(function() { //等待dom加载完后启动angular
    angularInit(document,bootstrap);
  });

三、dom加载前的准备工作

1.bindJQuery

这里将bindJQuery的代码贴出来,看看

function bindJQuery() {
  // bind to jQuery if present;
  jQuery = window.jQuery;
  // reset to jQuery or default to us.
  if (jQuery) { //如果使用jQuery,对其做了些扩展处理
    jqLite = jQuery;
    extend(jQuery.fn,{
      scope: JQLitePrototype.scope,isolateScope: JQLitePrototype.isolateScope,controller: JQLitePrototype.controller,injector: JQLitePrototype.injector,inheritedData: JQLitePrototype.inheritedData
    });

     //下面是对jquery改变dom时,增加一些清理工作,包括remove(删除元素),empty(置空),html(重写元素内容时)
    // Method signature:
    //     jqLitePatchJQueryRemove(name,dispatchThis,filterElems,getterIfNoArguments)
    jqLitePatchJQueryRemove('remove',true,false);
    jqLitePatchJQueryRemove('empty',false,false);
    jqLitePatchJQueryRemove('html',true);
  } else {
    jqLite = JQLite;
  }
  angular.element = jqLite;
}

2.publishExternalAPI

下面把publishExternalAPI的代码也贴出来

function publishExternalAPI(angular){
  extend(angular,{  //导出工具函数,就是直接将要导出的函数和属性加到angular对象上。细心的同学会发现下面少了angular.module的module。
    'bootstrap': bootstrap,'copy': copy,'extend': extend,'equals': equals,'element': jqLite,'forEach': forEach,'injector': createInjector,'noop':noop,'bind':bind,'toJson': toJson,'fromJson': fromJson,'identity':identity,'isUndefined': isUndefined,'isDefined': isDefined,'isstring': isstring,'isFunction': isFunction,'isObject': isObject,'isNumber': isNumber,'isElement': isElement,'isArray': isArray,'version': version,'isDate': isDate,'lowercase': lowercase,'uppercase': uppercase,'callbacks': {counter: 0},'$$minerr': minerr,'$$csp': csp
  });

  angularModule = setupModuleLoader(window); //这里需要重点分析
  try {
    angularModule('ngLocale');
  } catch (e) {
    angularModule('ngLocale',[])
    .provider('$locale',$LocaleProvider);
  }

  angularModule('ng',['ngLocale'],['$provide',//定义“ng”模块,$provide作为后面函数的依赖注入对象,可见$provide是在之前的模块中定义的
    function ngModule($provide) {

      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
      $provide.provider({ //注册$$sanitizeUri服务
        $$sanitizeUri: $$SanitizeUriProvider
      });

      $provide
      .provider('$compile',$CompileProvider) //定义$compile服务
      .directive({ //注册各种指令
            a: htmlAnchorDirective,input: inputDirective,textarea: inputDirective,form: formDirective,script: scriptDirective,select: selectDirective,style: styleDirective,option: optionDirective,ngBind: ngBindDirective,ngBindHtml: ngBindHtmlDirective,ngBindTemplate: ngBindTemplateDirective,ngClass: ngClassDirective,ngClassEven: ngClassEvendirective,ngClassOdd: ngClassOddDirective,ngCloak: ngCloakDirective,ngController: ngControllerDirective,ngForm: ngFormDirective,ngHide: ngHideDirective,ngIf: ngIfDirective,ngInclude: ngIncludeDirective,ngInit: ngInitDirective,ngNonBindable: ngNonBindableDirective,ngpluralize: ngpluralizeDirective,ngRepeat: ngRepeatDirective,ngShow: ngShowDirective,ngStyle: ngStyleDirective,ngSwitch: ngSwitchDirective,ngSwitchWhen: ngSwitchWhenDirective,ngSwitchDefault: ngSwitchDefaultDirective,ngOptions: ngOptionsDirective,ngTransclude: ngTranscludeDirective,ngModel: ngModelDirective,ngList: ngListDirective,ngChange: ngChangeDirective,required: requiredDirective,ngrequired: requiredDirective,ngValue: ngValueDirective
        })
        .directive({ //注册ngInclude 指令
          ngInclude: ngIncludeFillContentDirective
        })
        .directive(ngAttributealiasDirectives) //?
        .directive(ngEventDirectives);//?

      $provide.provider({ //注册剩余的各种服务
        $anchorScroll: $AnchorScrollProvider,$animate: $AnimateProvider,$browser: $browserProvider,$cacheFactory: $CacheFactoryProvider,$controller: $ControllerProvider,$document: $DocumentProvider,$exceptionHandler: $ExceptionHandlerProvider,$filter: $FilterProvider,$interpolate: $InterpolateProvider,$interval: $IntervalProvider,$http: $HttpProvider,$httpBackend: $HttpBackendProvider,$location: $LocationProvider,$log: $LogProvider,$parse: $ParseProvider,$rootScope: $RootScopeProvider,$q: $QProvider,$sce: $sceprovider,$sceDelegate: $SceDelegateProvider,$sniffer: $SnifferProvider,$templateCache: $TemplateCacheProvider,$timeout: $TimeoutProvider,$window: $WindowProvider
      });
    }
  ]);
}

细心的同学会发现:在导出的工具函数数组中少了angular.module的module,先不管,往下看。
我在这里重点分析setupModuleLoader函数以及angularModule对象到底是啥

function setupModuleLoader(window) {

  var $injectorminerr = minerr('$injector');
  var ngminerr = minerr('ng');

  function ensure(obj,name,factory) {
    return obj[name] || (obj[name] = factory());
  }

  var angular = ensure(window,'angular',Object);

  angular.$$minerr = angular.$$minerr || minerr;

  return ensure(angular,'module',function() {
    var modules = {};

    return function module(name,requires,configFn) {...};
  });
}

ensure(obj,factory)函数的含义:如果obj对象上存在name属性,直接返回;如果不存在,通过factory进行构造。
那么,angularModule = setupModuleLoader(window)执行后,会在angular上增加一个module属性,并且返回给angularModule。这里就把前面缺少的angular.module给加上了。

下面是module(name,configFn)的具体代码实现(为节约篇幅,省去注释):

function module(name,configFn) {
  var assertNotHasOwnProperty = function(name,context) {
    if (name === 'hasOwnProperty') {
      throw ngminerr('badname','hasOwnProperty is not a valid {0} name',context);
    }
  };

  assertNotHasOwnProperty(name,'module');
  if (requires && modules.hasOwnProperty(name)) {
    modules[name] = null;
  }
  return ensure(modules,function() {
    if (!requires) {
      throw $injectorminerr('nomod',"Module '{0}' is not available! You either misspelled " +
         "the module name or forgot to load it. If registering a module ensure that you " +
         "specify the dependencies as the second argument.",name);
    }  
    var invokeQueue = [];  
    var runBlocks = [];
    var config = invokelater('$injector','invoke');  
    var moduleInstance = {
      // Private state
      _invokeQueue: invokeQueue,_runBlocks: runBlocks,requires: requires,//依赖模块
      name: name,provider: invokelater('$provide','provider'),factory: invokelater('$provide','factory'),service: invokelater('$provide','service'),value: invokelater('$provide','value'),constant: invokelater('$provide','constant','unshift'),animation: invokelater('$animateProvider','register'),filter: invokelater('$filterProvider',controller: invokelater('$controllerProvider',directive: invokelater('$compileProvider','directive'),config: config,run: function(block) {
        runBlocks.push(block);
        return this;
      }
    };

    if (configFn) {
      config(configFn);
    }
    return  moduleInstance;  
    function invokelater(provider,method,insertMethod) {
      return function() {
        invokeQueue[insertMethod || 'push']([provider,arguments]);
        return moduleInstance;
      };
    }
  });
}

分析invokelater函数:这个函数的功能是返回一个函数,这个函数能向invokeQueue数组中插入一个三元组(provider,arguments)
那么,就是说provider、factory等函数的功能都是向_invokeQueue压入数据
`module().run(xx_function)将把xx_function压入_runBlocks队列数组中。
问题来了:这些将在哪里得到“执行”呢?

四、dom加载后的工作:angularInit(document,bootstrap)

1.angularInit的源码:

function angularInit(element,bootstrap) {
  var elements = [element],appElement,module,names = ['ng:app','ng-app','x-ng-app','data-ng-app'],NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;

  function append(element) {
    element && elements.push(element);
  }

  forEach(names,function(name) {
    names[name] = true;
    append(document.getElementById(name));
    name = name.replace(':','\\:');
    if (element.querySelectorAll) {
      forEach(element.querySelectorAll('.' + name),append);
      forEach(element.querySelectorAll('.' + name + '\\:'),append);
      forEach(element.querySelectorAll('[' + name + ']'),append);
    }
  });

  forEach(elements,function(element) {
    if (!appElement) {
      var className = ' ' + element.className + ' ';
      var match = NG_APP_CLASS_REGEXP.exec(className);
      if (match) {
        appElement = element;
        module = (match[2] || '').replace(/\s+/g,',');
      } else {
        forEach(element.attributes,function(attr) {
          if (!appElement && names[attr.name]) {
            appElement = element;
            module = attr.value;
          }
        });
      }
    }
  });
  if (appElement) {
    bootstrap(appElement,module ? [module] : []);
  }
}

上面的代码写了很多,但是只干了两件事:1.在dom中寻找启动节点,读出启动节点定义的启动模块的名字;2.如果找到了启动节点,用启动节点和其定义的启动模块的名字为参数调用bootstrap。

2.启动器bootstrap

function bootstrap(element,modules) {
  var dobootstrap = function() {
    element = jqLite(element);

    if (element.injector()) {
      var tag = (element[0] === document) ? 'document' : startingTag(element);
      throw ngminerr('btstrpd',"App Already Bootstrapped with this Element '{0}'",tag);
    }

    modules = modules || [];
    modules.unshift(['$provide',function($provide) { //将['$provide',function(){...}]压入模块数组
      $provide.value('$rootElement',element);
    }]);
    modules.unshift('ng'); //将ng压入模块数组
    var injector = createInjector(modules); //创建注入器
    injector.invoke(['$rootScope','$rootElement','$compile','$injector','$animate',//注入根作用域、根元素、编译器、注入器、动画
       function(scope,element,compile,injector,animate) { 
        scope.$apply(function() { //在根作用域上启动事件循环
          element.data('$injector',injector); //在根元素上保存注入器
          compile(element)(scope); //编译根元素
        });
      }]
    );
    return injector;
  };

  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;

  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
    return dobootstrap();
  }

  window.name = window.name.replace(NG_DEFER_BOOTSTRAP,'');
  angular.resumeBootstrap = function(extraModules) {
    forEach(extraModules,function(module) {
      modules.push(module);
    });
    dobootstrap();
  };
}

angular源码分析:完整加载流程的更多相关文章

  1. HTML实现代码雨源码及效果示例

    这篇文章主要介绍了HTML实现代码雨源码及效果示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. 使用layui实现左侧菜单栏及动态操作tab项的方法

    这篇文章主要介绍了使用layui实现左侧菜单栏及动态操作tab项的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. 在iOS上绘制扭曲的文本

    使用iOS9及更高版本中提供的标准API,如何在绘制文本时实现扭曲效果?

  5. ios – 如果Element符合给定的协议,则扩展阵列以符合协议

    如果是这样,语法是什么?解决方法Swift4.2在Swift4.2中,我能够使用符合这样的协议的元素扩展数组:

  6. ios – 如何在swift中获取2数组的常见元素列表

    (双关语)编辑:,你可以这样做这个实现是丑陋的.

  7. 在IOS9中的Cordova应用程序使用JQuery / Javascript的window.history问题

    在两个测试用例中唯一改变的是Cordova.js.解决方法我看到这是几个星期前,但我会发布这个,以防其他人遇到它.听起来它可能与iOS9中的哈希更改生成的导航事件有关.如果是这样,可以将其添加到index.html以禁用哈希侦听:

  8. iOS 5上的jQuery事件

    解决方法在Apple开发论坛上由一个人回答:我需要在将元素添加到DOM之后才绑定(),如下所示:

  9. 源码推荐:简化Swift编写的iOS动画,iOS Material Design库

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  10. swift皮筋弹动发射飞机ios源码

    这是一个款采用swift实现的皮筋弹动发射飞机游戏源码,游戏源码比较详细,大家可以研究学习一下吧。

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部