angularJS的源代碼整體上來說是一個自執行函數,在angularJS加載完成后,就會自動執行了。
angular源代碼中:
angular = window.angular || (window.angular = {})
定義一個全局的angular空對象。
然后:
bindJQuery(); //綁定jQuery publishExternalAPI(angular); //擴展angular對象的方法和屬性 jqLite(document).ready(function() { angularInit(document, bootstrap); });
var injector = angular.injector(); //得到一個注入器實例對象,它總共有5個方法:annotate,get,has,instantiate,invoke。annotate是用來分析函數的簽名的,在angular進行依賴注入的時候,你的函數的參數,你不需要new出來這個參數的對象,你只要告訴它你的函數需要什么東西,angular需要使用這個annotate來分析這些函數的參數。
angular啟動總共有三種方法:
第一種:默認方式,在頁面的元素節點上添加ng-app。angular會自動啟動。
第二種:不在頁面上添加任何的ng-app,我們手動啟動:
angular.element(document).ready(function(){
angular.bootstrap(document, ["myModule"]); //myModule是你創建的模塊的名字
});
第三種:在頁面上,添加兩個ng-app,但是必須在並行的兩個元素上添加。而且,angular只會自動啟動第一個ng-app,第二個ng-app需要你按照第二種方法,手動啟動。但是這種方式,會打印出異常。這種方式,基本上不會使用,可以忽略。
function bindJQuery() { // bind to jQuery if present; jQuery = window.jQuery; // reset to jQuery or default to us. if (jQuery) { jqLite = jQuery; extend(jQuery.fn, { scope: JQLitePrototype.scope, isolateScope: JQLitePrototype.isolateScope, controller: JQLitePrototype.controller, injector: JQLitePrototype.injector, inheritedData: JQLitePrototype.inheritedData }); // Method signature: // jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) jqLitePatchJQueryRemove('remove', true, true, false); jqLitePatchJQueryRemove('empty', false, false, false); jqLitePatchJQueryRemove('html', false, false, true); } else { jqLite = JQLite; } angular.element = jqLite; }
如果你引入了jQuery,就使用引入的jQuery。如果你沒有引入外部的jQuery,那么就使用angular自帶的JQLite。最后,設置angular.element = jqLite;
publishExternalAPI方法中,最重要的是setupModuleLoader方法:給angular構建模塊加載器。
function setupModuleLoader(window) { function ensure(obj, name, factory) { return obj[name] || (obj[name] = factory()); } var angular = ensure(window, 'angular', Object); //設置window.angular等於一個空對象 return ensure(angular, 'module', function() { //把angular.module設置成這個module函數,並返回這個函數。 var modules = {}; //當我們使用angular.module創建一個模塊時,都會緩存在變量modules中。 return function module(name, requires, configFn) { //當我們通過var demo1 = angular.module('demoApp', []);創建一個模塊時,它返回的是moduleInstance。而這個moduleInstance對象有factory(),controller(),directive(),config(),run()等方法可以調用。 if (requires && modules.hasOwnProperty(name)) { //如果有同名的模塊已經創建過,就把以前的那個模塊刪除。這里使用的是一個閉包,因為每次調用angular.module進行模塊的創建時,訪問的modules對象是在外層的匿名函數中定義的,本來一個函數執行結束后,就會銷毀里面的變量,雖然這里匿名函數已經執行結束了,但是由於內部函數module引用了此變量modules,所以即便外層的匿名函數執行結束了,它里面定義的modules變量也不會銷毀。通過閉包,我們可以定義一個私有的變量modules,只能通過規定的方法angular.module進行訪問,外部無法操作這個私有的變量modules。 modules[name] = null; } return ensure(modules, name, function() { //modules[demoApp] = moduleInstance,並返回這個moduleInstance。 var invokeQueue = []; var runBlocks = []; var config = invokeLater('$injector', 'invoke'); var moduleInstance = { //模塊實例的方法 requires: requires, 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'), //當我們通過一個模塊實例創建一個過濾器時,調用的是invokeLater方法返回的匿名函數function(){ invokeQueue['push']([$filterProvider, register, arguments]); return moduleInstance; } controller: invokeLater('$controllerProvider', 'register'), directive: invokeLater('$compileProvider', 'directive'), config: config, run: function(block) { runBlocks.push(block); return this; } }; if(configFn){ //當調用angular.module方法傳入了三個參數時,就會執行config方法,上面在定義ng模塊時,就會傳入第三個參數。 config(configFn); //config方法其實就是invokeLater方法執行后的返回值。這里執行之后,也是對數組invokeQueue進行push操作。當ng模塊創建時,invokeQueue = [ [ $injector, invoke, [[$provide, function ngModule(){}]] ] ]。 } return moduleInstance; function invokeLater(provider, method, insertMethod) { return function() { invokeQueue[insertMethod || 'push']([provider, method, arguments]); return moduleInstance; }; } }); }; }); }
當我們定義了一個var myModule = angular.module("myModule", [] );模塊時,通過模塊myModule調用controller,directive,service方法分別創建控制器,指令,服務的這些方法都是在上面的moduleInstance對象中定義的。這里的myModule就是moduleInstance對象的一個實例。
publishExternalAPI方法構建好模塊加載器之后,會先把自己的內核模塊加載起來,比如:把ng模塊,ngLocale模塊注冊進來,同時,會把一些內核的指令注冊進來。
function publishExternalAPI(angular){ extend(angular, { //綁定方法到angular對象上 'bootstrap': bootstrap, 'extend': extend, 'element': jqLite, 'injector': createInjector, ...... }); angularModule = setupModuleLoader(window); // 此方法會把angular對象綁定到window上,然后把一個函數綁定到angular的module屬性上,最后返回這個函數,這個函數是一個模塊加載器,主要作用是創建和獲取模塊。這里的angularModule函數就是angular.module函數。 try { angularModule('ngLocale'); } catch (e) { angularModule('ngLocale', []).provider('$locale', $LocaleProvider); //創建一個名為ngLocale的模塊,並在這個模塊上定義一個名為$locale的$LocaleProvider服務提供者。這里的provider方法,是把方法中的參數都存到invokeQueue數組中,以便在后面調用,從setupModuleLoader方法中很容易知道。 } angularModule('ng', ['ngLocale'], ['$provide', //創建一個名為ng的模塊,這個模塊依賴於ngLocale模塊。 function ngModule($provide) { $provide.provider({ $$sanitizeUri: $$SanitizeUriProvider }); $provide.provider('$compile', $CompileProvider). //ng模塊中,定義一個名為$compile的$CompileProvider服務提供者 directive({ a: htmlAnchorDirective, input: inputDirective, textarea: inputDirective, form: formDirective, option: optionDirective, ngBind: ngBindDirective, ngClass: ngClassDirective, ngController: ngControllerDirective, ngForm: ngFormDirective, ngHide: ngHideDirective, ngIf: ngIfDirective, ngInit: ngInitDirective, ngRepeat: ngRepeatDirective, ngShow: ngShowDirective, ngOptions: ngOptionsDirective, ngTransclude: ngTranscludeDirective, ngModel: ngModelDirective, ngList: ngListDirective, ngChange: ngChangeDirective, required: requiredDirective, ngRequired: requiredDirective, ngValue: ngValueDirective }); $provide.provider({ //在ng模塊中,定義一系列的服務提供者 $animate: $AnimateProvider, $controller: $ControllerProvider, $filter: $FilterProvider, $http: $HttpProvider, $location: $LocationProvider, $parse: $ParseProvider, $rootScope: $RootScopeProvider, $window: $WindowProvider }); } ]); }
最后,就是angularInit方法:此方法會去判斷頁面上是否有ng-app,如果有,就調用bootstrap方法進行啟動。如果沒有,你就需要自己手動去啟動了。
function angularInit(element, bootstrap) { var elements = [element], appElement, module, names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], //angular有4種方式在頁面上定義angular應用 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); } }); //針對4種定義方式,在頁面上獲取定義為angular應用的元素節點, forEach(elements, function(element) { if (!appElement) { //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; } }); } } }); //定義angular應用的元素,也就是在頁面上寫有ng-app(有4種方式定義)的元素定義為appElement。 if (appElement) { bootstrap(appElement, module ? [module] : []); //如果頁面上有定義angular應用的元素,就啟動。 } }
調用bootstrap方法,才代表angular真正開始啟動:
function bootstrap(element, modules) { var doBootstrap = function() { //定義一個函數 element = jqLite(element); if (element.injector()) { //如果此元素的angular應用已經啟動過了,就拋出錯誤 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.value('$rootElement', element); }]); modules.unshift('ng'); //這里,我們假設在頁面上定義了ng-app的元素,沒有添加任何的多余的東西。因此這里的modules=["ng",["$provide",function($provide){}]]。 var injector = createInjector(modules); //這個方法非常重要,它把我們angular應用需要初始化的模塊數組傳進去后,進行加載,並創建一個注冊器實例對象,最后返回這個注冊器實例對象。 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate', //調用注冊器實例對象的invoke方法 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)) { //如果window.name不是以NG_DEFER_BOOTSTRAP!開頭的話,就進入if語句,執行上面定義的方法。 return doBootstrap(); } window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); angular.resumeBootstrap = function(extraModules) { forEach(extraModules, function(module) { modules.push(module); }); doBootstrap(); }; }
上面的代碼會創建一個注入器,也就是說angular在啟動時,會創建一個注入器。一個angular應用只有一個注入器。當創建好注入器后,它會首先把內核加載起來,通過compile方法。
加油!