javascript
AngularJS深入(1)——加载启动
本系列文章的分析基于AngularJS?v1.4.2.
參考資料有:
- AngularJS API Docs
- AngularJS Developer Guide
- AngularJS實戰(zhàn)
- Service vs provider vs factory
1. 整體結(jié)構(gòu)
AngularJS的源碼在整體上,與其它很多庫和框架一樣,是一個自執(zhí)行函數(shù),其整體結(jié)構(gòu)簡化如下:
(function(window, document, undefined) {// define variables and functions// and do some operationsif (window.angular.bootstrap) {console.log('WARNING: Tried to load angular more than once.');return;}bindJQuery();publishExternalAPI(angular);jqLite(document).ready(function() {angularInit(document, bootstrap);}); })(window, document);?
整體思路為:
- 首先是一些全局變量和方法的定義,以及一些其它操作;
- 通過window.angular.bootstrap判斷是否已經(jīng)加載angular,如果已經(jīng)加載,則直接退出;
- 執(zhí)行bindJQuery(),如果已經(jīng)加載了jQuery,則AngularJS會使用已經(jīng)加載的jQuery,否則使用內(nèi)部實現(xiàn)的JQLite,其相當(dāng)于是一個簡化版的jQuery;
- 執(zhí)行publishExternalAPI(angular)來為全局變量angular增加屬性和方法,并建立起模塊機(jī)制,注冊核心模塊;
- 在文檔加載完成后執(zhí)行angularInit()。
2. bindJQuery
該方法主要是綁定jQuery,簡化后的代碼如下:
var bindJQueryFired = false;function bindJQuery() {if (bindJQueryFired) {return;}var jqName = jq();jQuery = window.jQuery;if (isDefined(jqName)) {jQuery = jqName === null ? undefined : window[jqName];}if (jQuery && jQuery.fn.on) {jqLite = jQuery;// ... ...} else {jqLite = JQLite;}angular.element = jqLite;bindJQueryFired = true; }- bindJQueryFired相當(dāng)于是一個標(biāo)志符,初始值為false。在執(zhí)行bindJQuery的時候,先判斷bindJQueryFired的值,如果其為true,則說明已經(jīng)執(zhí)行過jQuery綁定,直接返回;否則執(zhí)行綁定過程,并將bindJQueryFired的值設(shè)置為true;
- jqName是調(diào)用jq()的返回值,jq()的主要作用是遍歷文檔,找出第一個包含屬性ng-jq的節(jié)點,然后取其屬性值;
- 變量jQuery取值為window.jQuery,如果加載了jQuery函數(shù)庫,則其值非空;
- 在應(yīng)用了ng-jq指令的情況下,如果jQName的值不為null,則設(shè)置變量jQuery的值為window[jqName],否則設(shè)置為undefined
- 如果jQuery變量有效,則使用jQuery變量指定的庫;否則使用內(nèi)置實現(xiàn)的JQLite。
總結(jié)起來,綁定的jQuery可以的來源有三個:ng-jq指定、引入的jQuery庫、內(nèi)置實現(xiàn)的JQLite,其使用流程為:
- 如果有ng-jq
- 如果ng-jq的值不為空,則使用它指定的庫
- 如果ng-jq指定的值為空,則變量jQuery的值為undefined,此時強(qiáng)制使用JQLite,無論是否引入了jQuery庫
- 如果沒有ng-jq
- 如果引入了jQuery庫,則使用它
- 如果沒有引入jQuery庫,則使用JQLite
3. publishExternalAPI
該方法的代碼簡化如下:
function publishExternalAPI(angular) {extend(angular, {// ... ... });angularModule = setupModuleLoader(window);try {angularModule('ngLocale');} catch (e) {angularModule('ngLocale', []).provider('$locale', $LocaleProvider);}angularModule('ng', ['ngLocale'], ['$provide',function ngModule($provide) {// ... ... }]); }主要功能為:
- 對angular對象進(jìn)行擴(kuò)展;
- 執(zhí)行setupModuleLoader(window),該方法主要是定義了angular.module方法,用于注冊及獲取模塊;angular.module只有一個參數(shù)的時候為獲取模塊,否則為注冊模塊;
- 如果沒有注冊ngLocal模塊,則對其進(jìn)行注冊;
- 注冊ng模塊,也就是AngularJS的核心模塊。
4. angularInit
該方法的主要作用是啟動Angular應(yīng)用。相關(guān)代碼為:
var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];function getNgAttribute(element, ngAttr) {var attr, i, ii = ngAttrPrefixes.length;for (i = 0; i < ii; ++i) {attr = ngAttrPrefixes[i] + ngAttr;if (isString(attr = element.getAttribute(attr))) {return attr;}}return null; }function angularInit(element, bootstrap) {var appElement,module,config = {};// The element `element` has priority over any other elementforEach(ngAttrPrefixes, function(prefix) {var name = prefix + 'app';if (!appElement && element.hasAttribute && element.hasAttribute(name)) {appElement = element;module = element.getAttribute(name);}});forEach(ngAttrPrefixes, function(prefix) {var name = prefix + 'app';var candidate;if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {appElement = candidate;module = candidate.getAttribute(name);}});if (appElement) {config.strictDi = getNgAttribute(appElement, "strict-di") !== null;bootstrap(appElement, module ? [module] : [], config);} }需要說明的是,AngularJS支持的屬性前綴有多種,包括ng-、data-ng、ng:和x-ng-。
- 首先對element進(jìn)行檢測,看它是否有ng-app等屬性,如果有則設(shè)定appElement和module;
- 如果element元素沒有ng-app等屬性,則對其后繼元素進(jìn)行查找,找到第一個有ng-app等屬性的元素,從而設(shè)定appElement和module;
- 如果appElement不為空,即找到了應(yīng)用的入口元素,則執(zhí)行bootstrap。
需要注意的是,如果有多個元素都有ng-app屬性,則只會找到第一個并啟動它,而后面的應(yīng)用則不會自動啟動。
5. 應(yīng)用啟動
應(yīng)用的啟動方式主要包括自動啟動和手動啟動。
(1)自動啟動
例如:
<div ng-app="MyModule"><div ng-controller="ctrl">{{ name }}</div> </div> <script>var myModule = angular.module('MyModule', []);myModule.controller('ctrl', ['$scope', function($scope) {$scope.name = 'alex';}]); </script>在這個例子中,對于最外層的div設(shè)置了ng-app屬性,因此會自動啟動應(yīng)用。
(2)手動啟動
例如:
<div><div ng-controller="ctrl"></div> </div> <script>var myModule = angular.module('MyModule', []);myModule.controller('ctrl', ['$scope', function($scope) {$scope.name = 'alex';}]);angular.element(document).ready(function() {angular.bootstrap(document, ['MyModule']);}); </script>?
由于沒有設(shè)置ng-app,因此需要通過angular.bootstrap來手動啟動應(yīng)用。
(3)多個應(yīng)用的啟動
一般情況下,一個頁面中只有一個應(yīng)用,但是一個頁面上多個應(yīng)用也是可以共存的。通過上面對源碼的分析,可以知道,只有第一個應(yīng)用會自動啟動,因此其余的應(yīng)用需要手動來啟動,例如:
<div id="app1" ng-app="MyModule1"><div ng-controller="ctrl1"></div> </div> <div id="app2" ng-app="MyModule2"><div ng-controller="ctrl2"></div> </div> <script>var myModule1 = angular.module('MyModule1', []);myModule1.controller('ctrl1', ['$scope', function($scope) {$scope.name = 'alex';}]);var myModule2 = angular.module('MyModule2', []);myModule2.controller('ctrl2', ['$scope', function($scope) {$scope.greeting = 'hello';}]);angular.element(document).ready(function() {angular.bootstrap(document.getElementById('app2'), ['MyModule2']);}); </script>?
?
轉(zhuǎn)載于:https://www.cnblogs.com/folyred/p/4723773.html
總結(jié)
以上是生活随笔為你收集整理的AngularJS深入(1)——加载启动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [资料]pthreads PHP
- 下一篇: UVA 11383 Golden Tig