AngularJS源码解析1:angular自启动过程

angularJS加载进来后,会有一个立即执行函数调用,在源代码的最下面是angular初始化的地方。代码展示:

bindJQuery();



publishExternalAPI(angular);



jqLite(document).ready(function() {

     angularInit(document, bootstrap);

});

bindJQuery方法的作用是:检查你的主页面是否引用了jquery,没有的话,就用angular本身自带的JQLite,否则使用你引用的jquery。

    function bindJQuery() {

        jQuery = window.jQuery;     //如果在主页面加载了jQuery库,这里的jQuery就会存在

        if (jQuery) {

            jqLite = jQuery;

       ......

        } else {

            jqLite = JQLite;    //如果没有引用jQuery库,就使用自带的JQLite。

        }

        angular.element = jqLite;      //把结果赋给angular.element上

    }

publishExternalAPI方法的作用是:首先绑定一些方法到angular对象上,然后就是初始化angular核心的模块。

function publishExternalAPI(angular){

        extend(angular, {              //绑定方法到angular对象上

              'bootstrap': bootstrap,

        'extend': extend,

        'element': jqLite,

        'injector': createInjector,         ...... }); angularModule = setupModuleLoader(window); // 此方法会把angular对象绑定到window上,然后把一个函数绑定到angular的module属性上,最后返回这个函数,这个函数是一个模块加载器,主要作用是创建和获取模块。这里的angularModule函数就是angular.module函数。 try { angularModule('ngLocale'); } catch (e) { angularModule('ngLocale', []).provider('$locale', $LocaleProvider); //创建一个名为ngLocale的模块,并在这个模块上定义一个名为$locale的$LocaleProvider服务提供者。这里的provider方法,是把方法中的参数都存到invokeQueue数组中,以便在后面调用,从setupModuleLoader方法中很容易知道。 } angularModule('ng', ['ngLocale'], ['$provide', //创建一个名为ng的模块,这个模块依赖于ngLocale模块。 function ngModule($provide) { $provide.provider({ $$sanitizeUri: $$SanitizeUriProvider }); $provide.provider('$compile', $CompileProvider). //ng模块中,定义一个名为$compile的$CompileProvider服务提供者 directive({ a: htmlAnchorDirective, input: inputDirective, textarea: inputDirective, form: formDirective, option: optionDirective, ngBind: ngBindDirective, ngClass: ngClassDirective, ngController: ngControllerDirective, ngForm: ngFormDirective, ngHide: ngHideDirective, ngIf: ngIfDirective, ngInit: ngInitDirective, ngRepeat: ngRepeatDirective, ngShow: ngShowDirective, ngOptions: ngOptionsDirective, ngTransclude: ngTranscludeDirective, ngModel: ngModelDirective, ngList: ngListDirective, ngChange: ngChangeDirective, required: requiredDirective, ngRequired: requiredDirective, ngValue: ngValueDirective }); $provide.provider({ //在ng模块中,定义一系列的服务提供者 $animate: $AnimateProvider, $controller: $ControllerProvider, $filter: $FilterProvider, $http: $HttpProvider, $location: $LocationProvider, $parse: $ParseProvider, $rootScope: $RootScopeProvider, $window: $WindowProvider }); } ]); }

setupModuleLoader方法的源码:

    function setupModuleLoader(window) {
    
function ensure(obj, name, factory) { return obj[name] || (obj[name] = factory()); } var angular = ensure(window, 'angular', Object); //设置window.angular等于一个空对象
     return ensure(angular, 'module', function() { //把angular.module设置成这个module函数,并返回这个函数。 var modules = {}; return function module(name, requires, configFn) { //当我们通过var demo1 = angular.module('demoApp', []);创建一个模块时,它返回的是moduleInstance。而这个moduleInstance对象有factory(),controller(),directive(),config(),run()等方法可以调用。
          if (requires && modules.hasOwnProperty(name)) {   //如果有同名的模块已经创建过,就把以前的那个模块删除。这里使用的是一个闭包,因为每次调用angular.module进行模块的创建时,访问的modules对象是在外层的匿名函数中定义的,本来一个函数执行结束后,就会销毁里面的变量,虽然这里匿名函数已经执行结束了,但是由于内部函数module引用了此变量modules,所以即便外层的匿名函数执行结束了,它里面定义的modules变量也不会销毁。通过闭包,我们可以定义一个私有的变量modules,只能通过规定的方法angular.module进行访问,外部无法操作这个私有的变量modules。

                    modules[name] = null;

                } 

                return ensure(modules, name, function() {      //modules[demoApp] = moduleInstance,并返回这个moduleInstance。

                    var invokeQueue = [];

                    var runBlocks = [];

                    var config = invokeLater('$injector', 'invoke');

                    var moduleInstance = {    //模块实例的方法

                        requires: requires,

                        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'),       //当我们通过一个模块实例创建一个过滤器时,调用的是invokeLater方法返回的匿名函数function(){    invokeQueue['push']([$filterProvider, register, arguments]);   return moduleInstance;    }

                        controller: invokeLater('$controllerProvider', 'register'),                  

                        directive: invokeLater('$compileProvider', 'directive'),               

                        config: config,               

                        run: function(block) {

                            runBlocks.push(block);

                            return this;

                        }

                    };
            
            if(configFn){ //当调用angular.module方法传入了三个参数时,就会执行config方法,上面在定义ng模块时,就会传入第三个参数。
              config(configFn); //config方法其实就是invokeLater方法执行后的返回值。这里执行之后,也是对数组invokeQueue进行push操作。当ng模块创建时,invokeQueue = [ [ $injector, invoke, [[$provide, function ngModule(){}]] ] ]。
            }
return moduleInstance; function invokeLater(provider, method, insertMethod) { return function() { invokeQueue[insertMethod || 'push']([provider, method, arguments]); return moduleInstance; }; } }); }; }); }

我们举个例子说下上面的源码:

var demo1 = angular.module('demoApp', []);

demo1.filter('reverse', function(){     当调用此方法时,其实就是调用invokeQueue["push"]()方法,并且返回此模块实例,以便进行链式操作。比如:demo1.filter().directive()。当调用demo1的filter方法时,这里会执行invokeQueue["push"]([  $filterProvider, register, ["reverse", function(){}] ]),而这只是一个数组的push操作,也就是说,invokeQueue = [  [ $filterProvider, register, ["reverse", function(){}] ]  ];

    return function(input, uppercase) {

      var out = "";

      for (var i = 0; i < input.length; i++) {

        out = input.charAt(i) + out;

      }

      if (uppercase) {

        out = out.toUpperCase();

      }

      return out;

    }

  });

publishExternalAPI方法讲完之后,接下来就是

jqLite(document).ready(function() {    //文档加载完成后,执行angularInit方法,我们经常说的:文档加载完成后,angular才启动的意思就是在这里。

        angularInit(document, bootstrap);

});
    function angularInit(element, bootstrap) {   

        var elements = [element],  

            appElement,

            module,

            names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],   //angular有4种方式在页面上定义angular应用

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

            }

        });    //针对4种定义方式,在页面上获取定义为angular应用的元素节点,

        forEach(elements, function(element) {

            if (!appElement) {    //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;

                        }

                    });

                }

            }

        });    //定义angular应用的元素,也就是在页面上写有ng-app(有4种方式定义)的元素定义为appElement。

        if (appElement) {     

            bootstrap(appElement, module ? [module] : []); //如果页面上有定义angular应用的元素,就启动。

        }

    }
    function bootstrap(element, modules) {

        var doBootstrap = function() {      //定义一个函数

            element = jqLite(element);

            if (element.injector()) {    //如果此元素的angular应用已经启动过了,就抛出错误

                var tag = (element[0] === document) ? 'document' : startingTag(element);

                throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);

            }

            modules = modules || [];

            modules.unshift(['$provide', function($provide) {

                $provide.value('$rootElement', element);

            }]);

            modules.unshift('ng');     //这里,我们假设在页面上定义了ng-app的元素,没有添加任何的多余的东西。因此这里的modules=["ng",["$provide",function($provide){}]]。

            var injector = createInjector(modules);   //这个方法非常重要,它把我们angular应用需要初始化的模块数组传进去后,进行加载,并创建一个注册器实例对象,最后返回这个注册器实例对象。

            injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',  //调用注册器实例对象的invoke方法

                function(scope, element, compile, injector, animate) {

                    scope.$apply(function() {

                        element.data('$injector', injector);

                        compile(element)(scope);    

                    });

                }]

            );

            return injector;     

        };

        var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;

        if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {   //如果window.name不是以NG_DEFER_BOOTSTRAP!开头的话,就进入if语句,执行上面定义的方法。

            return doBootstrap();     

        }

        window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');

        angular.resumeBootstrap = function(extraModules) {

            forEach(extraModules, function(module) {

                modules.push(module);

            });

            doBootstrap();

        };

    }

上面的invoke方法执行后,会调用compile方法,此方法里面会先生成编译实例,然后通过编译实例去编译应用的元素,编译的核心是生成指令对应的link函数。

 

 

 

 

加油!

你可能感兴趣的:(AngularJS)