AngularJS深入(1)——加载启动

本系列文章的分析基于AngularJS v1.4.2.

参考资料有:

1. 整体结构

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()

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相当于是一个标志符,初始值为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

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对象进行扩展;
  • 执行setupModuleLoader(window),该方法主要是定义了angular.module方法,用于注册及获取模块;angular.module只有一个参数的时候为获取模块,否则为注册模块;
  • 如果没有注册ngLocal模块,则对其进行注册;
  • 注册ng模块,也就是AngularJS的核心模块。

4. angularInit

该方法的主要作用是启动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-ngng:x-ng-

  • 首先对element进行检测,看它是否有ng-app等属性,如果有则设定appElementmodule
  • 如果element元素没有ng-app等属性,则对其后继元素进行查找,找到第一个有ng-app等属性的元素,从而设定appElementmodule;
  • 如果appElement不为空,即找到了应用的入口元素,则执行bootstrap

需要注意的是,如果有多个元素都有ng-app属性,则只会找到第一个并启动它,而后面的应用则不会自动启动。

5. 应用启动

应用的启动方式主要包括自动启动和手动启动。

(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>

 

时间: 2024-10-09 23:28:01

AngularJS深入(1)——加载启动的相关文章

模块的加载启动

Sea.js 是一个模块加载器,模块加载器需要实现两个基本功能: 实现模块定义规范,这是模块系统的基础. 模块系统的启动与运行. 模块定义规范的实现 这就是 define,require,exports,module 的实现.具体实现细节,有兴趣的可以看 Sea.js 的源码:seajs/src.可以按照 Gruntfile.js 中声明的合并顺序阅读,核心是 module.js 文件. define 等方法的具体使用,请阅读:CMD 模块定义规范 模块系统的启动 有了 define 等模块定义

004-ant design pro安装、目录结构、项目加载启动

一.概述 1.1.脚手架概念 编程领域中的“脚手架(Scaffolding)”指的是能够快速搭建项目“骨架”的一类工具.例如大多数的React项目都有src,public,webpack配置文件等等,而src目录中又包含components目录等等.每次在新建项目时,手动创建这些固定的文件目录.脚手架的作用就是帮助你完成这些重复性的工作,包括一键生成主要的目录结构.安装依赖等等. 1.2.参考地址 ant design参看地址:https://ant.design/docs/react/intr

以及ajax以及angularjs 动态模板加载并进行渲染

1. 源码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script type="text/javascript" src="js/jquery-1.11.1.min.js"></script> <script type="text/java

angularjs 路由 异步加载js

这个问题 首要出现 在 当我 把require 引入 项目中是,希望做到 点击路由时加载相应的页面html和 js. 起初我是将 route 的 controller 设置为 require('controller.js') 来 异步加载, 当然,问题是不可能这么简单的,肯定要出现一些问题. 这样就出现一个问题,当 我路由子页面 中出现 与数据相关的 初始化时,第一次跳转是正常的,此时跳到其他页面,再跳回该页面时 controller的数据 却无法获取. 导致页面空白. $routeProvid

angularjs ocLazyLoad分步加载js文件,angularjs ocLazyLoad按需加载js

用angular有一段时间了,平日里只顾着写代码,没有注意到性能优化的问题,而今有时间,于是捋了捋,讲学习过程记录于此: 问题描述:由于采用angular做了网页的单页面应用,需要一次性在主布局中将所有模块需要引用到的js都引入.对于比较小的项目,这是可行的,但是对于大的项目,一旦js文件较多,在页面首次加载时就引入所有js文件,无疑会延缓页面加载的速度,造成不良额用户体验.那么分布加载(按需加载)就显得很有必要了. <!DOCTYPE html> <html lang="en

word取消加载启动项

打开word文件一栏选择选项 选择加载项 点击转到即可选择性取消 原文地址:https://www.cnblogs.com/ybl20000418/p/11508692.html

swift——启动页国际化:一步一步动态加载启动页图片,启动的时候加载文字

由于公司的需求,要求做一个国际化的启动页,因为app我也弄国际化了,就剩下启动页国际化未完成,百度了呵谷歌了好多答案都不尽如人意,最后也是看见同事完成,我也问了具体的做法,决定分享给需要的人,免得和我当初一样着急,浪费半天时间,我实现的效果要么就是中文版显示正常,英文版显示不正常,要么就是中文版显示中文的文字,要么就是英文版的显示中文,反正不是需求的结果,最后借鉴同事的做法,得以解决. 废话不多说,首先,得准备一套图片:              可能截图不对,这三张图片2xhe3x图片各两套,

应用程序程序启动时加载广告 ---ios

app 在启动的时候会加载启动图片,我们怎么样去做一个广告页面呢? 思路:在启动完毕后,创建一个加载广告的控制器,然后等广告结束后,我们在去加载主框架内容 如图: 首先我们先创建一个AD的控制器 @interface ABADViewController () @property (weak, nonatomic) IBOutlet UIButton *ADJumpBtn; //跳过 按钮 @property (weak, nonatomic) IBOutlet UIImageView *ADI

AngularJS中多个ng-app(手动加载模块)

1.当有多个ng-app时:(首先是要加载angularJS) <div ng-app=""> <p>姓名:<input type="text" ng-model="name" placeholder="请输入姓名" /></p> <p> {{name}} </p> </div> <div ng-app="">