因爲在博客系統的開發中和近期工做中的前端框架主要使用 AngularJS ,所以在這裏記錄學習和使用 AngularJS 的過程當中遇到的一些須要記錄的點。特別說明,本文並不是教程。html
弄清楚 AngularJS 的執行過程是很重要的,這樣你才能在正確的時機作正確的事。在這點上我就犯過錯誤,話很少說,直接上代碼:前端
var app = angular.module('app', ['ngRoute']); app.config([ '$routeProvider', '$http', '$q', function ($routeProvider, $http, $q) { $routeProvider .when('/', { template: '123', resolve: { auth: function () { // do stuff } } }); } ]);
報錯啦!!!上面的代碼在啓動階段就會報下圖所示的錯誤:bootstrap
乍一看都不知道錯在哪裏,通過分析才知道,module.config 方法是在 on module loading,即模塊加載過程當中執行的,此時 $http 和 $q 等服務都尚未建立成功,不能當作依賴項注入到 module.config 方法中。數組
回到主題,AngularJS 框架的執行過程大體以下所示:前端框架
配合源碼會理解的更清楚:app
bindJQuery(); publishExternalAPI(angular); jqLite(document).ready(function() { angularInit(document, bootstrap); });
具體代碼能夠到源碼中查看,這裏簡要說明一下:框架
bindJQuery()
嘗試綁定jQuery對象,若是沒有則採用內置的jqLite。ide
publishExternalAPI(angular)
初始化 angular
環境,爲 angular
對象註冊 module
,forEach
,extend
等方法。函數
關於 module
方法,在此要說明一下:學習
angular.module('myApp')
只傳一個參數,爲getter操做,返回 moduleInstance
對象,而 angular.module('myApp',[]) 傳入兩個參數,爲setter操做,也返回 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; } }
angularInit(document, bootstrap)
方法內容以下:
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; };
最後經過 $apply 將做用域轉入 angular 做用域,所謂angular做用域是指:angular採用dirity-check方式進行檢測,達到雙向綁定。
再利用 compile 函數編譯整個頁面文檔,識別出 directive,按照優先級排序,執行他們的 compilie 函數,最後返回 link function 的結合,經過 scope 與模板鏈接起來,造成一個即時,雙向綁定。
至此,AngularJS 的執行過程也就告一段落了。