[关闭]
@yiranphp 2016-04-08T18:14:22.000000Z 字数 4781 阅读 3475

angularJs启动分析

angular


启动流程图

Created with Raphaël 2.1.2 立即执行 一个“自执行函数表达式 检查是否多次导入angular: 尝试绑定jQery:bindJQuery() publishExternalAPI(angular) angularInit(document, bootstrap); 查找ng-app bootstrap(element, modules, config) 需要手动启动angular程序yesno

1、整体结构

一个自执行函数,简化如下

  1. (function(window, document, undefined) {
  2. // 定义变量和函数
  3. // 其他操作
  4. if (window.angular.bootstrap) {
  5. console.log('WARNING: Tried to load angular more than once.');
  6. return;
  7. }
  8. bindJQuery();
  9. publishExternalAPI(angular);
  10. jqLite(document).ready(function() {
  11. angularInit(document, bootstrap);
  12. });
  13. })(window, document);

整体思路为:

  1. 首先是一些全局变量和方法的定义,以及一些其它操作;
  2. 通过window.angular.bootstrap判断是否已经加载angular,如果已经加载,则直接退出;
  3. 执行bindJQuery(),如果已经加载了jQuery,则AngularJS会使用已经加载的jQuery,否则使用内部实现的JQLite,其相当于是一个简化版的jQuery;
  4. 执行publishExternalAPI(angular)来为全局变量angular增加属性和方法,并建立起模块机制,注册核心模块;
  5. 在文档加载完成后执行angularInit()。

2、bindJQuery

绑定jQuery,简化如下

  1. var bindJQueryFired = false;
  2. function bindJQuery() {
  3. if (bindJQueryFired) {
  4. return;
  5. }
  6. var jqName = jq();
  7. jQuery = window.jQuery;
  8. if (isDefined(jqName)) {
  9. jQuery = jqName === null ? undefined : window[jqName];
  10. }
  11. if (jQuery && jQuery.fn.on) {
  12. jqLite = jQuery;
  13. // ... ...
  14. } else {
  15. jqLite = JQLite;
  16. }
  17. angular.element = jqLite;
  18. bindJQueryFired = true;
  19. }
  1. bindJQueryFired相当于是一个标志符,初始值为false。在执行bindJQuery的时候,先判断bindJQueryFired的值,如果其为true,则说明已经执行过jQuery绑定,直接返回;否则执行绑定过程,并将bindJQueryFired的值设置为true;
  2. jqName是调用jq()的返回值,jq()的主要作用是遍历文档,找出第一个包含属性ng-jq的节点,然后取其属性值;
  3. 变量jQuery取值为window.jQuery,如果加载了jQuery函数库,则其值非空;
  4. 在应用了ng-jq指令的情况下,如果jQName的值不为null,则设置变量jQuery的值为window[jqName],否则设置为undefined
  5. 如果jQuery变量有效,则使用jQuery变量指定的库;否则使用内置实现的JQLite。

总结起来,绑定的jQuery可以的来源有三个:ng-jq指定、引入的jQuery库、内置实现的JQLite,其使用流程为:

如果有ng-jq

  1. 如果ng-jq的值不为空,则使用它指定的库
  2. 如果ng-jq指定的值为空,则变量jQuery的值为undefined,此时强制使用JQLite,无论是否引入了jQuery库

如果没有ng-jq

如果引入了jQuery库,则使用它; 如果没有引入jQuery库,则使用JQLite

3. publishExternalAPI

代码简化如下

  1. function publishExternalAPI(angular) {
  2. extend(angular, {
  3. // ... ...
  4. });
  5. angularModule = setupModuleLoader(window);
  6. try {
  7. angularModule('ngLocale');
  8. } catch (e) {
  9. angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
  10. }
  11. angularModule('ng', ['ngLocale'], ['$provide',
  12. function ngModule($provide) {
  13. // ... ...
  14. }
  15. ]);
  16. }

主要功能:
1. 扩展angular对象,挂载工具函数
2. 执行setupModuleLoader(window),该方法主要是创建了模块加载器:angular.module方法,用于注册及获取模块;angular.module只有一个参数的时候为获取模块,否则为注册模块;
3. 如果没有注册ngLocal模块,则对其进行注册;
4. 注册ng模块,也就是AngularJS的核心模块。

4. angularInit

启动Angular应用,相关代码为:

  1. jqLite(document).ready(function() {
  2. angularInit(document, bootstrap);
  3. });
  1. // angularInit定义
  2. var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
  3. function getNgAttribute(element, ngAttr) {
  4. var attr, i, ii = ngAttrPrefixes.length;
  5. for (i = 0; i < ii; ++i) {
  6. attr = ngAttrPrefixes[i] + ngAttr;
  7. if (isString(attr = element.getAttribute(attr))) {
  8. return attr;
  9. }
  10. }
  11. return null;
  12. }
  13. function angularInit(element, bootstrap) {
  14. var appElement,
  15. module,
  16. config = {};
  17. // The element `element` has priority over any other element
  18. forEach(ngAttrPrefixes, function(prefix) {
  19. var name = prefix + 'app';
  20. if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
  21. appElement = element;
  22. module = element.getAttribute(name);
  23. }
  24. });
  25. forEach(ngAttrPrefixes, function(prefix) {
  26. var name = prefix + 'app';
  27. var candidate;
  28. if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
  29. appElement = candidate;
  30. module = candidate.getAttribute(name);
  31. }
  32. });
  33. if (appElement) {
  34. config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
  35. bootstrap(appElement, module ? [module] : [], config);
  36. }
  37. }

需要说明的是,AngularJS支持的属性前缀有多种,包括ng-、data-ng、ng:和x-ng-。

  1. 首先对element进行检测,看它是否有ng-app等属性,如果有则设定appElementmodule
  2. 如果element元素没有ng-app等属性,则对其后代元素进行查找,找到第一个有ng-app等属性的元素,从而设定appElement和module;
  3. 如果appElement不为空,即找到了应用的入口元素,则执行bootstrap
  4. 需要注意的是,如果有多个元素都有ng-app属性,则只会找到第一个并启动它,而后面的应用则不会自动启动。

启动总结:自动启动、手动启动
1、自动启动:

  1. <body ng-app="plunker">
  2. <div ng-controller="MainCtrl">
  3. <p>Hello {{name}}!</p>
  4. </div>
  5. </body>
  6. <script>
  7. var app = angular.module('plunker', []);
  8. app.controller('MainCtrl', ['$scope', function($scope) {
  9. $scope.name = 'alex';
  10. }]);
  11. </script>

上面的例子使用了ng-app指令:http://plnkr.co/edit/HJNL9gDbECjX0SsU4IIG?p=info

2、手动启动

  1. <body>
  2. <div ng-controller="MainCtrl">
  3. <p>Hello {{name}}!</p>
  4. </div>
  5. </body>
  6. <script>
  7. var app = angular.module('plunker', []);
  8. app.controller('MainCtrl', ['$scope', function($scope) {
  9. $scope.name = 'alex';
  10. }]);
  11. angular.element(document).ready(function() {
  12. angular.bootstrap(document.body, ['plunker']);
  13. });
  14. </script>

上面的例子没有使用ng-app指令,用angular.bootStrap启动:http://plnkr.co/edit/DFHhpwA0fKzERF2JgDJn?p=info

3、多启动:首先是应用是不能嵌套的,如果使用了多个ng-app,只有第一个是自动启动

  1. <body>
  2. <div ng-controller="MainCtrl1" ng-app="app1">
  3. <p>Hello {{name}}!</p>
  4. </div>
  5. <div ng-controller="MainCtrl2" ng-app="app2" id="app2">
  6. <p>Hello {{name}}!</p>
  7. </div>
  8. </body>
  9. <script>
  10. var app1 = angular.module('app1', []);
  11. app1.controller('MainCtrl1', ['$scope', function($scope) {
  12. $scope.name = 'alex';
  13. }]);
  14. var app2 = angular.module('app2', []);
  15. app2.controller('MainCtrl2', ['$scope', function($scope) {
  16. $scope.name = 'yiranphp';
  17. }]);
  18. angular.element(document).ready(function() {
  19. angular.bootstrap(document.getElementById('app2'), ['app2']);
  20. });
  21. </script>

上面的例子第一个ng-app自动启动,第二个手动启动:http://plnkr.co/edit/xnNqm7NSQKRegGkAshTH?p=preview

本篇文章到此结束,欢迎指正

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注