angularjs源碼分析之:angularjs執行流程

來源:互聯網
上載者:User

標籤:c   style   class   blog   code   java   

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

稍後分析,……

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.