angularjs源码分析之:angularjs执行流程

angularjs用了快一个月了,最难的不是代码本身,而是学会怎么用angular的思路思考问题。其中涉及到很多概念,比如:directive,controller,service,compile,link,scope,isolate scope,双向绑定,mvvm等。最近准备把这些都慢慢搞懂,分析源码并贴到博客园,如有分析不对的地方,还望各位包容并指正。

angularjs源码分析之:angularjs执行流程

先上个大图,有个大概印象,注:angularjs的版本为:1.2.1,通过bower install angularjs安装的。

 

几个重要方法

  bindJQuery();

  publishExternalAPI(angular);

  jqLite(document).ready(function() {
    angularInit(document, bootstrap);
  });

20121行,bindJQuery,尝试绑定jQuery对象,如果没有则采用内置的jqLite

20123行,publishExternalAPI,初始化angular环境。

     1820-1848行,把一些基础api挂载到angular上,如:extend,forEach,isFunction等。

     1850行,angularModule = setupModuleLoader(window);  此方法为模块加载器,在angular上添加了module方法,最后返回的实质上是:

         angular.module = function module(name,require,configFn);

         当我们angular.module(‘myApp‘),只传一个参数,为getter操作,返回moduleInstance

         当我们angular.module(‘myApp‘,[]) 时返回对象moduleInstance

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‘, ‘register‘),
          controller: invokeLater(‘$controllerProvider‘, ‘register‘),
          directive: invokeLater(‘$compileProvider‘, ‘directive‘),
          config: config,
          run: function(block) {
            runBlocks.push(block);
            return this;
          }
}

因为这样,我们才可以链式操作 ,如:  .factory().controller(). 

这里需要重点提到两个函数:ensure 和 invokeLater。

通过ensure(obj,nam,factory)可以实现:先检索obj.name,如果有就是getter操作,否则为setter操作。

此机制完成了在windows对象上声明angular属性,在angular对象上声明module属性。

通过invokeLater可以返回一个闭包,当调用config,provider(即moduleInstance返回的那些api)调用时,实质上就是调用这个闭包,拿

app.provider(‘myprovider‘,[‘$window‘,function($window){ //code}]) 举例,此api调用后,会将:

(‘$provide‘,‘provider‘,[‘$window‘,function($window){}]) push进invokeQueue数组中,注意此处只是入队操作,并未执行。在后面执行时,实际上市通过 第一个参数调用第二个参数方法名,把第三个参数当变量传入,即:args[0][args[1]].apply(args[0],args[2]);具体后面会分析到。

20125,domready后调用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] : []);
  }
}

遍历names,通过document.getElementById(name) 或者是 querySelectorAll(name)检索到 element后存入 elements数组中,最后获取到appElement以及module。举个例子:我们一般会在文档开始的html标签上写 ng-app="myApp".通过以上方法,我们最后可以得到 名为myApp的module,后调用bootstrap(appElement,[module]);

bootstrap中需要重点关注 doBootstrap方法

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 = [‘myApp‘];
    modules = modules || [];
//添加$provide这个数组 modules.unshift([
‘$provide‘, function($provide) { $provide.value(‘$rootElement‘, element); }]);
//添加 ng这个 module ,注意:1857行 我们注册过ng 这个module,
并在1854行 我们注册过 它的依赖模块‘ngLocale‘,
    //angularModule(‘ngLocale‘, []).provider(‘$locale‘, $LocaleProvider); 我们注册过ngLocale这个module
    modules.unshift(‘ng‘);
//调用createInjector(module) 此时:module为:
//[‘ng‘,[‘$provide‘,function(){}],‘myApp‘] 两个type为string,一个为array
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; };

createInjector是重点,拿出来单独分析

function createInjector(modulesToLoad) {
  var INSTANTIATING = {},
      providerSuffix = ‘Provider‘,
      path = [],
      loadedModules = new HashMap(),
      providerCache = {
        $provide: {
            provider: supportObject(provider),
            factory: supportObject(factory),
            service: supportObject(service),
            value: supportObject(value),
            constant: supportObject(constant),
            decorator: decorator
          }
      },
      providerInjector = (providerCache.$injector =
          createInternalInjector(providerCache, function() {
            throw $injectorMinErr(‘unpr‘, "Unknown provider: {0}", path.join(‘ <- ‘));
          })),
      instanceCache = {},
      instanceInjector = (instanceCache.$injector =
          createInternalInjector(instanceCache, function(servicename) {
            var provider = providerInjector.get(servicename + providerSuffix);
            return instanceInjector.invoke(provider.$get, provider);
          }));


  forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });

  return instanceInjector;
/**
...省略若干
**/

* 主要是四个变量:

providerCache,providerInjector,instanceCache,instancheInjector

providerCache初始化只有一个对象 providerCache = { $provide:{}} ,紧接着调用createInternalInjector 方法返回一个若干重两级api(annotate,get等)赋值给providerCache.$injector 以及provoderInjector.则结果就变成这样了:

providerCache = {
        $provide: {
            provider: supportObject(provider),
            factory: supportObject(factory),
            service: supportObject(service),
            value: supportObject(value),
            constant: supportObject(constant),
            decorator: decorator
          }
      },
      $injector:{
          get:getService,
          annotate:annotate,
          instantiate:instantiate,
          invoke:invoke,
          has:has
      }
}

而providerInjector变成了这样:

providerInjector:{
      nvoke: invoke,
      instantiate: instantiate,
      get: getService,
      annotate: annotate,
      has: has
}

同样,instanceCache和instanceInjector变成:

instanceCache:{
      $injector:{
          invoke: invoke,
          instantiate: instantiate,
          get: getService,
          annotate: annotate,
          has: has
      }
}


instanceInjector = {
      invoke: invoke,
      instantiate: instantiate,
      get: getService,
      annotate: annotate,
      has: has
}

 

* 两个重要方法:

loadModules,createInternalInjector

稍后分析,……

 

 

angularjs源码分析之:angularjs执行流程,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。