AngularJS-1.启动流程

整体结构

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标签的元素。


  1. 引用jQuery的前提下,和$用法基本相同:angular.element(‘#id’).hide(),不引入jQuery,则功能较弱angular.element(document).find(‘xx’).hide()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值