AngularJS-1.啟動流程
阿新 • • 發佈:2019-01-31
整體結構
AngularJS的原始碼在整體上,與其它很多庫和框架一樣,是一個自執行函式,其整體結構簡化如下:
(function(window, document, undefined) {
if (window.angular.bootstrap) {//判斷是否已經啟動
console.log('WARNING: Tried to load angular more than once.');
return;
}
bindJQuery();//繫結JQuery
publishExternalAPI(angular);//對外公佈AngularJS的函式
jqLite(document).ready(function() {
angularInit(document, bootstrap);//啟動AngularJS
});
})(window, document);
整體思路
- 首先是一些全域性變數和方法的定義,以及一些其它操作;
- 通過window.angular.bootstrap判斷是否已經載入angular,如果已經載入,則直接退出;
- 執行bindJQuery(),如果已經載入了jQuery,則AngularJS會使用已經載入的jQuery,否則使用內部實現的JQLite,其相當於是一個簡化版的jQuery;
- 執行publishExternalAPI(angular)來為全域性變數angular增加屬性和方法,並建立起模組機制,註冊核心模組;
在文件載入完成後執行angularInit()。
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;
}
檢視angular.element1
- bindJQueryFired相當於是一個標誌符,初始值為false。在執行bindJQuery的時候,先判斷bindJQueryFired的值,如果其為true,則說明已經執行過jQuery繫結,直接返回;否則執行繫結過程,並將bindJQueryFired的值設定為true;
- jqName是呼叫jq()的返回值,jq()的主要作用是遍歷文件,找出第一個包含屬性ng-jq的節點,然後取其屬性值;
- 變數jQuery取值為window.jQuery,如果載入了jQuery函式庫,則其值非空;
- 在應用了ng-jq指令的情況下,如果jQName的值不為null,則設定變數jQuery的值為window[jqName],否則設定為undefined
如果jQuery變數有效,則使用jQuery變數指定的庫;否則使用內建實現的JQLite。
總結起來,繫結的jQuery可以的來源有三個:ng-jq指定、引入的jQuery庫、內建實現的JQLite,其使用流程為:
- 如果有ng-jq
– 如果ng-jq的值不為空,則使用它指定的庫
– 如果ng-jq指定的值為空,則變數jQuery的值為undefined,此時強制使用JQLite,無論是否引入了jQuery庫 - 如果沒有ng-jq
– 如果引入了jQuery庫,則使用它
– 如果沒有引入jQuery庫,則使用JQLite
publishExternalAPI
該方法的程式碼簡化如下:
function publishExternalAPI(angular) {
//拓展angular物件
extend(angular, {
......
});
//定義angular.module方法
angularModule = setupModuleLoader(window);
//嘗試獲取ngLocal模組,如果沒有,則需要註冊一個
try {
angularModule('ngLocale');
} catch (e) {
angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
}
//註冊ng模組,也就是angularJS的核心模組
angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) {
// ... ...
}
]);
}
主要功能為:
- 對angular物件進行擴充套件;
- 執行setupModuleLoader(window),該方法主要是定義了angular.module方法,用於註冊及獲取模組;angular.module只有一個引數的時候為獲取模組,否則為註冊模組;
- 如果沒有註冊ngLocal模組,則對其進行註冊;
- 註冊ng模組,也就是AngularJS的核心模組。
angularInit
function angularInit(element, bootstrap) {
var appElement,
module,
config = {};
//先判斷element元素是否包含ng-app屬性
forEach(ngAttrPrefixes, function(prefix) {
var name = prefix + 'app';
if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
appElement = element;
module = element.getAttribute(name);
}
});
//再判斷element元素的子元素中是否包含ng-app屬性
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-,具體請看ngAttrPrefixes變數。
- 首先對element進行檢測,看它是否有ng-app等屬性,如果有則設定appElement和module;
- 如果element元素沒有ng-app等屬性,則對其子元素進行查詢,找到第一個有ng-app等屬性的元素,從而設定appElement和module;
- 如果appElement不為空,即找到了應用的入口元素,則執行bootstrap。
需要注意的是,如果有多個元素都有ng-app屬性,則只會找到第一個並啟動它,而後面的應用則不會自動啟動。因為這個函式主動執行一次。
應用啟動
應用的啟動方式主要包括自動啟動和手動啟動。
自動啟動
<div ng-app="MyModule">
<div ng-controller="ctrl">
</div>
</div>
<script>
var myModule = angular.module('MyModule', []);
myModule.controller('ctrl', ['$scope', function($scope) {
$scope.name = 'alex';
}]);
</script>
手動啟動
<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>
注意 :需要在ready函式中呼叫啟動函式,因為如果文件沒有載入完成,則angular無法掃描到含有ng-app標籤的元素。
- 引用jQuery的前提下,和$用法基本相同:angular.element(‘#id’).hide(),不引入jQuery,則功能較弱angular.element(document).find(‘xx’).hide() ↩