AngularJS 的啓動過程分析

Angular 啓動過程分析.png

本文例子所使用版本爲 v1.3.9javascript

啓動過程

步驟1

  • 用自執行函數的形式讓整個代碼在加載完成以後當即執行html

    inangular.js Line6java

    javascript(function(window, document, undefined)
  • 在window上暴露一個惟一的全局對象angular,Line250angularjs

    /** @name angular */
        //若是window.angular已經有值了,就把原有的賦值給前面angular,若是沒有則用window.angular空對象賦值給angular
    angular = window.angular || (window.angular = {}),
        angularModule,
        uid = 0;
  • 得到其它工具模塊 Line 2129bootstrap

    function publishExternalAPI(angular) {
      extend(angular, {
        'bootstrap': bootstrap,
        'copy': copy,
        'extend': extend,
        'equals': equals,
        'element': jqLite,
        'forEach': forEach,
        'injector': createInjector,
        'noop': noop,
        'bind': bind,
        .....
  • 咱們來看看angular全局對象都有什麼東西緩存

    Alt text

  • 接着,咱們使用全局對象中的isFunction 來遍歷一下angular全局對象上的屬性,函數閉包

    var counter = 0;
    for (var p in angular) {
        counter++;
        if (angular.isFunction(angular[p])) {
            console.log("function->" + p);
        } else {
            console.log("property-->" + p + "-->" + angular[p]);
        }
    }
    console.log(counter);

    有兩個property,其它都是functionapp

    Alt text

  • 咱們再來看看injector裏都有什麼ide

    /**
     * angular.injector();
     */
    var injector = angular.injector();
    console.log(injector);

一共有5個方法
annotate:分析函數簽名(不要new的緣由)函數

Alt text

步驟2

  • 檢查是否是屢次導入Angular:window.angular.bootstrap(經過檢查指定的元素上是否已經存在injector進行判斷)

    in angular.js line 26041

    if (window.angular.bootstrap) {
        //AngularJS is already loaded, so we can return here...
        console.log('WARNING: Tried to load angular more than once.');
        return;
      }

Angular 的三種啓動方式

方式 1:自動啓動

Angular會自動的找到ng-app,將它做爲啓動點,自動啓動

<!DOCTYPE html>
<html ng-app="myModule">

<head>
    <title>New Page</title>
    <meta charset="utf-8" />
    <script type="text/javascript" src="../../vendor/bower_components/angular/angular.min.js"></script>
    <script type="text/javascript" src="./02.boot1.js"></script>
</head>

<body>
    <div ng-controller="MyCtrl">
        <span>{{Name}}</span>
    </div>
</body>
</html>

JS

var myModule = angular.module("myModule", []);
myModule.controller('MyCtrl', ['$scope',
    function($scope) {
        $scope.Name = "Puppet";
    }
]);

方式 2:手動啓動

在沒有ng-app的狀況下,只須要在js中添加一段註冊代碼便可

<body>
    <div ng-controller="MyCtrl">
        <span>{{Name}}</span>
    </div>
</body>

JS

var myModule = angular.module("myModule", []);
myModule.controller('MyCtrl', ['$scope',
    function($scope) {
        $scope.Name = "Puppet";
    }
]);

/**
 * 這裏要用ready函數等待文檔初始化完成
 */
angular.element(document).ready(function() {
    angular.bootstrap(document, ['myModule']);
});

方式 3:多個ng-app

ng中,angular的ng-app是沒法嵌套使用的,在不嵌套的狀況下有多個ng-app,他默認只會啓動第一個ng-app,第二個第三個須要手動啓動(注意,不要手動啓動第一個,雖然能夠運行,但會拋異常)

<body>
    <div id="app1" ng-app="myModule1">
        <div ng-controller="MyCtrl">
            <span>{{Name}}</span>
        </div>
    </div>
    <div id="app2" ng-app="myModule2">
        <div ng-controller="MyCtrl">
            <span>{{Name}}</span>
        </div>
    </div>
</body>

JS

/**
 * 第一個APP
 * @type {[type]}
 */
var myModule1 = angular.module("myModule1", []);
myModule1.controller('MyCtrl', ['$scope',
    function($scope) {
        $scope.Name = "Puppet";
    }
]);
// angular.element(document).ready(function() {
//     angular.bootstrap(app1, ['MyModule1']);
// });

/**
 * 第二個APP
 * @type {[type]}
 */
var myModule2 = angular.module("myModule2", []);
myModule2.controller('MyCtrl', ['$scope',
    function($scope) {
        $scope.Name = "Vincent";
    }
]);
angular.element(document).ready(function() {
    angular.bootstrap(app2, ['myModule2']);
});

步驟3

嘗試綁定jQuery,若是發現導入了jQuery ,則使用導入的jQuery,不然,使用Angular本身封裝的JQLite

in angular.js line 1521:

bindJQuery();

in angular.js line 1534:

jQuery = window.jQuery;

if (jQuery && jQuery.fn.on) {
    jqLite = jQuery;
    extend(jQuery.fn, {
      scope: JQLitePrototype.scope,
      isolateScope: JQLitePrototype.isolateScope,
      controller: JQLitePrototype.controller,
      injector: JQLitePrototype.injector,
      inheritedData: JQLitePrototype.inheritedData
    });

測試

<html ng-app="myModule">


<body>
    <div>
        <div ng-controller="MyCtrl">
            <span>{{Name}}</span>
        </div>
    </div>
</body>

</html>

JS

var myModule = angular.module("myModule", []);
myModule.controller('MyCtrl', ['$scope',
    function($scope) {
        $scope.Name = "Puppet";
    }
]);

查看執行流程

Alt text

步驟4

發佈ng提供的API

in angular.js line 2162 : publishExternalAPI(angular);

//構建模塊加載器
angularModule = setupModuleLoader(window);
  try {
    angularModule('ngLocale');
  } catch (e) {
    angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
  }

模塊加載器的實現原理

in angular.js function setupModuleLoader(window)

function setupModuleLoader(window) {

  var $injectorMinErr = minErr('$injector');
  var ngMinErr = minErr('ng');

  function ensure(obj, name, factory){
  return obj[name] || (obj[name] = factory());
  }
  var angular = ensure(window, 'angular', Object);
  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
  angular.$$minErr= angular.$ $minErr || minErr;
  //把module方法放到angular的全局對象上,ensure就是一個屬性拷貝的過程
return ensure(angular, 'module', function(){
    //模塊緩存
    var modules = {};
}

把工具函數給載到模塊裏

javascriptreturn function module(name, requires, configFn) {
......
}

查看模塊裏所包含的內容

<!DOCTYPE html>
<html ng-app="myModule">

</html>

JS

/**
 * 定義模塊
 */
var myModule = angular.module("myModule", []);

/**
 * 從debug的過程能夠看到,angular中的「模塊」只是一個閉包空間(或者叫命名空間)
 * 全部模塊都被註冊在modules這個對象上
 */
console.log(myModule);

查看控制檯

Alt text

構建內置模塊 ng

in angular.js line 2169 publishExternalAPI(angular)

angularModule('ng', ['ngLocale'], ['$provide',
    function ngModule($provide) {
      // $ $sanitizeUriProvider needs to be before $compileProvider as it is used by it.
      $provide.provider({
        $ $sanitizeUri: $$SanitizeUriProvider
      });
      $provide.provider('$compile', $CompileProvider).
        directive({
            a: htmlAnchorDirective,
            input: inputDirective,
            ......

加載了全部內置的directive,provider.註冊ng內核Provider:兩個最重要的$parser$rootScope

總結

  1. 工具函數拷貝到angular全局對象上;
  2. 調用setupModuleLoader方法建立模塊定義和加載工具(掛在全局對象window.angular上);
  3. 構建內置模塊ng;
  4. 建立ng內置的directive和provider;
  5. 兩個重要的provider:$parse$rootScope

步驟5

初始化Angular - 查找Ng-app

javascriptjqLite(document).ready(function() {
    angularInit(document, bootstrap);
  });

bootstrap

建立injector,拉起內核和啓動模塊,調用compile服務 (一個ng-app只有一個injector)

in angular.js line 1415

function bootstrap(element, modules, config){
....
}

本文原載於@Puppet 原創學習筆記,如需轉載請附上 原文連接>>
logo.jpg

相關文章
相關標籤/搜索