AngularJS学习笔记(1) --- 执行过程

前言

因为在博客系统的开发中和近期工做中的前端框架主要使用 AngularJS ,所以在这里记录学习和使用 AngularJS 的过程当中遇到的一些须要记录的点。特别说明,本文并不是教程。html

执行过程

弄清楚 AngularJS 的执行过程是很重要的,这样你才能在正确的时机作正确的事。在这点上我就犯过错误,话很少说,直接上代码:前端

var app = angular.module('app', ['ngRoute']);

app.config([
    '$routeProvider',
    '$http',
    '$q',
    function ($routeProvider, $http, $q) {
        $routeProvider
            .when('/', {
                template: '123',
                resolve: {
                    auth: function () {
                        // do stuff
                    }
                }
            });
    }
]);

报错啦!!!上面的代码在启动阶段就会报下图所示的错误:bootstrap

启动报错

乍一看都不知道错在哪里,通过分析才知道,module.config 方法是在 on module loading,即模块加载过程当中执行的,此时 $http 和 $q 等服务都尚未建立成功,不能当作依赖项注入到 module.config 方法中。数组

回到主题,AngularJS 框架的执行过程大体以下所示:前端框架

执行过程

配合源码会理解的更清楚:app

bindJQuery();

publishExternalAPI(angular);

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

具体代码能够到源码中查看,这里简要说明一下:框架

  • bindJQuery() 尝试绑定jQuery对象,若是没有则采用内置的jqLite。ide

  • publishExternalAPI(angular) 初始化 angular 环境,为 angular 对象注册 moduleforEachextend 等方法。函数

    关于 module 方法,在此要说明一下:学习

    angular.module('myApp') 只传一个参数,为getter操做,返回 moduleInstance 对象,而 angular.module('myApp',[]) 传入两个参数,为setter操做,也返回 moduleInstance 对象

    var moduleInstance = {
        // Private state
        _invokeQueue: invokeQueue,
        _runBlocks: runBlocks,
        requires: requires,
        name: name,
        provider: invokeLater('$provide', 'provider'),
        factory: invokeLater('$provide', 'factory'),
        service: invokeLater('$provide', 'service'),
        value: invokeLater('$provide', 'value'),
        constant: invokeLater('$provide', 'constant', 'unshift'),
        animation: invokeLater('$animateProvider', 'register'),
        filter: invokeLater('$filterProvider', 'register'),
        controller: invokeLater('$controllerProvider', 'register'),
        directive: invokeLater('$compileProvider', 'directive'),
        config: config,
        run: function(block) {
            runBlocks.push(block);
            return this;
        }
    }
  • angularInit(document, bootstrap) 方法内容以下:

    function angularInit(element, bootstrap) {
      var elements = [element],
          appElement,
          module,
          names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
          NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
    
      function append(element) {
        element && elements.push(element);
      }
    
      forEach(names, function(name) {
        names[name] = true;
        append(document.getElementById(name));
        name = name.replace(':', '\\:');
        if (element.querySelectorAll) {
          forEach(element.querySelectorAll('.' + name), append);
          forEach(element.querySelectorAll('.' + name + '\\:'), append);
          forEach(element.querySelectorAll('[' + name + ']'), append);
        }
      });
    
      forEach(elements, function(element) {
        if (!appElement) {
          var className = ' ' + element.className + ' ';
          var match = NG_APP_CLASS_REGEXP.exec(className);
          if (match) {
            appElement = element;
            module = (match[2] || '').replace(/\s+/g, ',');
          } else {
            forEach(element.attributes, function(attr) {
              if (!appElement && names[attr.name]) {
                appElement = element;
                module = attr.value;
              }
            });
          }
        }
      });
      if (appElement) {
        bootstrap(appElement, module ? [module] : []);
      }
    }

    遍历names,经过 document.getElementById(name) 或者是 querySelectorAll(name) 检索到 element 后存入 elements 数组中,最后获取到 appElement 以及module。

    举个例子:咱们通常会在文档开始的html标签上写 ng-app="myApp",经过以上方法,咱们最后能够获得名为 myApp 的 module,后调用 bootstrap(appElement,[module]);

    bootstrap 中须要重点关注 doBootstrap 方法:

    var doBootstrap = function() {
      element = jqLite(element);
    
      if (element.injector()) {
        var tag = (element[0] === document) ? 'document' : startingTag(element);
        throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
      }
      //经过上面分析咱们知道此时 modules 暂时是这样的: modules = ['myApp'];
      modules = modules || [];
      //添加$provide这个数组
      modules.unshift(['$provide', function($provide) {
        $provide.value('$rootElement', element);
      }]);
      //添加 ng这个 module ,注意:1857行  咱们注册过ng 这个module,并在1854行 咱们注册过 它的依赖模块'ngLocale',
      //angularModule('ngLocale', []).provider('$locale', $LocaleProvider); 咱们注册过ngLocale这个module
      modules.unshift('ng');
      //调用createInjector(module) 此时:module为:
      //['ng',['$provide',function(){}],'myApp']  两个type为string,一个为array
      var injector = createInjector(modules);
      injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
         function(scope, element, compile, injector, animate) {
          scope.$apply(function() {
            element.data('$injector', injector);
            compile(element)(scope);
          });
        }]
      );
      return injector;
    };

最后经过 $apply 将做用域转入 angular 做用域,所谓angular做用域是指:angular采用dirity-check方式进行检测,达到双向绑定。

再利用 compile 函数编译整个页面文档,识别出 directive,按照优先级排序,执行他们的 compilie 函数,最后返回 link function 的结合,经过 scope 与模板链接起来,造成一个即时,双向绑定。

至此,AngularJS 的执行过程也就告一段落了。

相关文章
相关标签/搜索