本系列文章的分析基于AngularJS v1.4.2.
参考资料有:
AngularJS的源码在整体上,与其它很多库和框架一样,是一个自执行函数,其整体结构简化如下:
(function(window, document, undefined) { // define variables and functions // and do some operations if (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判断是否已经加载angular,如果已经加载,则直接退出;bindJQuery(),如果已经加载了jQuery,则AngularJS会使用已经加载的jQuery,否则使用内部实现的JQLite,其相当于是一个简化版的jQuery;publishExternalAPI(angular)来为全局变量angular增加属性和方法,并建立起模块机制,注册核心模块;angularInit()。该方法主要是绑定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相当于是一个标志符,初始值为false。在执行bindJQuery的时候,先判断bindJQueryFired的值,如果其为true,则说明已经执行过jQuery绑定,直接返回;否则执行绑定过程,并将bindJQueryFired的值设置为true;jqName是调用jq()的返回值,jq()的主要作用是遍历文档,找出第一个包含属性ng-jq的节点,然后取其属性值;jQuery取值为window.jQuery,如果加载了jQuery函数库,则其值非空;ng-jq指令的情况下,如果jQName的值不为null,则设置变量jQuery的值为window[jqName],否则设置为undefinedjQuery变量有效,则使用jQuery变量指定的库;否则使用内置实现的JQLite。总结起来,绑定的jQuery可以的来源有三个:ng-jq指定、引入的jQuery库、内置实现的JQLite,其使用流程为:
ng-jq
ng-jq的值不为空,则使用它指定的库ng-jq指定的值为空,则变量jQuery的值为undefined,此时强制使用JQLite,无论是否引入了jQuery库ng-jq
该方法的代码简化如下:
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对象进行扩展;setupModuleLoader(window),该方法主要是定义了angular.module方法,用于注册及获取模块;angular.module只有一个参数的时候为获取模块,否则为注册模块;ngLocal模块,则对其进行注册;ng模块,也就是AngularJS的核心模块。该方法的主要作用是启动Angular应用。相关代码为:
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 element forEach(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进行检测,看它是否有ng-app等属性,如果有则设定appElement和module;element元素没有ng-app等属性,则对其后继元素进行查找,找到第一个有ng-app等属性的元素,从而设定appElement和module;appElement不为空,即找到了应用的入口元素,则执行bootstrap。需要注意的是,如果有多个元素都有ng-app属性,则只会找到第一个并启动它,而后面的应用则不会自动启动。
应用的启动方式主要包括自动启动和手动启动。
(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设置了ng-app属性,因此会自动启动应用。
(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>
由于没有设置ng-app,因此需要通过angular.bootstrap来手动启动应用。
(3)多个应用的启动
一般情况下,一个页面中只有一个应用,但是一个页面上多个应用也是可以共存的。通过上面对源码的分析,可以知道,只有第一个应用会自动启动,因此其余的应用需要手动来启动,例如:
<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>
原文:http://www.cnblogs.com/folyred/p/4723773.html