angularjs-1.3代码学习 模块

来源:互联网 发布:品质退款率被淘宝管控 编辑:程序博客网 时间:2024/06/08 10:03

花了点时间,阅读了下angularjs的源码。本次先从模块化开始。

angular可以通过module的api来实现前端代码的模块化管理。跟define类似。但不具备异步加载脚本的功能。先从最基本的module开始。查看代码发现angular对module的定义是在setupModuleLoader方法中定义的

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;  return ensure(angular, 'module', function() {        return function module(name, requires, configFn) {      //省略.....      return ensure(modules, name, function() {     //省略...        return moduleInstance;      });    };  });}

可以看到源码中是通过ensure函数,将module定义到angular,并且在函数内返回函数,这就使用到了闭包,angular的目的是为了存储对应的module信息。在整个angular模块系统中,源码通过modules来存储所有用户定义的模块。当用户对自己定义的模块注册服务时(factory,service,provider,value时),这些服务会被存在各自模块内部的invokeQueue数组中维护。这同样是一个闭包。

当setupModuleLoader方法执行结束之后,返回的值是一个moduleInstance。这也是我们在外部调用angular.module时返回的值,看一下内部结构:

var moduleInstance = {          // Private state          _invokeQueue: invokeQueue,          _configBlocks: configBlocks,          _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;          }        };

我们可以使用_invokeQueue属性查看,当前module下面注册了哪些服务。另外moduleInstance也向外部公开了诸如provider,factory.service这些接口,供用户去注册自定义服务。至于如何注册的,是通过其内部的invokeLater方法,统统加入到invokeQueue数组中

function invokeLater(provider, method, insertMethod, queue) {          if (!queue) queue = invokeQueue;          return function() {            queue[insertMethod || 'push']([provider, method, arguments]);            return moduleInstance;          };        }

目前,我们大致了解了模块的定义和一般服务的注册。大致调用代码如下:

var app = angular.module("A", []); //定义一个模块Aapp.service("A1", function(){            console.log(1);});  //在模块A上定义一个服务A1

ok,万事俱备,只欠业炎。我们如何使用它?angular提供了一个injector的方法,我们来看一下。从内部函数publishExternalAPI中看到injector的定义,它来源于angular的一个叫做createInInjector的函数。

function createInjector(modulesToLoad, strictDi) {  strictDi = (strictDi === true);  var INSTANTIATING = {},      providerSuffix = 'Provider',      path = [],      loadedModules = new HashMap([], true),      providerCache = {        $provide: {            provider: supportObject(provider),            factory: supportObject(factory),            service: supportObject(service),            value: supportObject(value),            constant: supportObject(constant),            decorator: decorator          }      };    var  providerInjector = (providerCache.$injector =          createInternalInjector(providerCache, function(serviceName, caller) {            if (angular.isString(caller)) {              path.push(caller);            }            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));          }))    var instanceCache = {};    var instanceInjector = (instanceCache.$injector =          createInternalInjector(instanceCache, function(serviceName, caller) {            var provider = providerInjector.get(serviceName + providerSuffix, caller);            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);          }));forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });  return instanceInjector; // 省略.....}

 可以看出,内部定义了两个核心对象providerCache和instanceCache。它们都是由createInternalInjector函数生成。那进入createInternalInjector函数里看一下

function createInternalInjector(cache, factory) {    //....return {      invoke: invoke,      instantiate: instantiate,      get: getService,      annotate: createInjector.$$annotate,      has: function(name) {          //对象本身是否有该属性        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);      }    };}

因为createInjector函数中最终返回的是instanceInjector,而此君指向的则是instanceCache的$injector属性,而这个属性则是createInternalInjector函数的返回值。换言之,用户可以在外部调用这些方法

var injector = angular.injector(["A"]);//其中injector就是createInternalInjector函数的返回的对象

在我们调用这些接口之前,需要告诉angular去执行哪个模块里的服务,让我们回到createInjector函数中,看一下函数最后的for循环

forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });

可以猜想这个loadModules函数的作用应该是获取模块。简单看一下loadModules

function loadModules(modulesToLoad) {    var runBlocks = [], moduleFn;    forEach(modulesToLoad, function(module) {      if (loadedModules.get(module)) return; //先尝试去loadedModules查找      loadedModules.put(module, true);      function runInvokeQueue(queue) {        var i, ii;        for (i = 0, ii = queue.length; i < ii; i++) {            //获取用户之前需要注册的服务          var invokeArgs = queue[i],              provider = providerInjector.get(invokeArgs[0]);          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);        }      }      try {        if (isString(module)) {          moduleFn = angularModule(module); //angularModule其实就是外部用户调用的module接口,获取到对应模块          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);//如果模块有依赖则事先获取依赖模块          runInvokeQueue(moduleFn._invokeQueue);//之前所有的声明的服务都被存在放这个模块的invokeQueue数组中          runInvokeQueue(moduleFn._configBlocks);        } else if (isFunction(module)) {            runBlocks.push(providerInjector.invoke(module));        } else if (isArray(module)) {            runBlocks.push(providerInjector.invoke(module));        } else {          assertArgFn(module, 'module');        }      } catch (e) {        if (isArray(module)) {          module = module[module.length - 1];        }        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {          // Safari & FF's stack traces don't contain error.message content          // unlike those of Chrome and IE          // So if stack doesn't contain message, we create a new string that contains both.          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.          /* jshint -W022 */          e = e.message + '\n' + e.stack;        }        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",                  module, e.stack || e.message || e);      }    });    return runBlocks;  }

在runInvokeQueue中,我们看到源码获取了之前用户存在放invokeQueue中的服务对象,并且通过get方法及getService方法去获取服务。这里有个细节地方,源码是通过providerInjector的get方法来获取的,那我们看getService源码的时候,其中的cache则应该是在之前createInjector函数中的providerCache。

function getService(serviceName, caller) {      if (cache.hasOwnProperty(serviceName)) {        if (cache[serviceName] === INSTANTIATING) {          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',                    serviceName + ' <- ' + path.join(' <- '));        }        return cache[serviceName];      } else {        try {          path.unshift(serviceName);          cache[serviceName] = INSTANTIATING;          return cache[serviceName] = factory(serviceName, caller);        } catch (err) {          if (cache[serviceName] === INSTANTIATING) {            delete cache[serviceName];          }          throw err;        } finally {          path.shift();        }      }    }

基本还是先尝试在cache中找一次,没有则创建并保存在cache中。因为之前providerInjector中就已经有了$provider何$inject这样的方法。所以在

var invokeArgs = queue[i],              provider = providerInjector.get(invokeArgs[0]);          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);

中,provider就是:

providerCache = {        $provide: {            provider: supportObject(provider),            factory: supportObject(factory),            service: supportObject(service),            value: supportObject(value),            constant: supportObject(constant),            decorator: decorator          }      };

然后,再有后面的一步provider[invokeArgs[1].apply(provider, invokeArgs[2])]。看到这里我们突然恍然大悟,开始angular为什么要使用

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'),

这类写法注册的原因了,也就是现在我们才真正调用具体service,factory或者是value等api去执行或者讲要初始化我们自定义的服务。这里我们顺便看一下factory,service和provider的内部实现,这些API以后我们会经常用到:

function provider(name, provider_) {    assertNotHasOwnProperty(name, 'service');    if (isFunction(provider_) || isArray(provider_)) {      provider_ = providerInjector.instantiate(provider_); //实例化    }    if (!provider_.$get) {      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);    }    return providerCache[name + providerSuffix] = provider_; //并存储在本地缓存中  }function factory(name, factoryFn, enforce) {    return provider(name, { //底层调用的是provider      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn    });  }   function service(name, constructor) {    return factory(name, ['$injector', function($injector) { //底层调用factory      return $injector.instantiate(constructor);    }]);  }  function value(name, val) { return factory(name, valueFn(val), false); } //底层调用factory

factory,service和value基本底层都是使用的provider方法。可以看到provider方法需要$get的这种键值对的方式将函数传入。所以我们在外部使用这一系列API的方式,大致

var app = angular.module("A", []);        app.service("A1", function(){            console.log(1);        });       app.factory("B1", [function(){           console.log(2);           return {};       }]);       app.provider("C1", {           $get: function(){               console.log(3);           }       });       app.value("D1", 4);       app.constant("E1", 5);

有兴趣的朋友可以试下,service使用factory的方法定义可以不可以^_^。至此,我们之前使用injector()方法实际上获取到了instanceInjector。目前我们还是没有触发或者执行我们定义的服务,实际上instanceInjector上提供了如下几个方法:

return {      invoke: invoke,      instantiate: instantiate,      get: getService,      annotate: createInjector.$$annotate,      has: function(name) {          //对象本身是否有该属性        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);      }    };

当我们执行get方法的时候,我们注册的服务的以执行!

function getService(serviceName, caller) {      if (cache.hasOwnProperty(serviceName)) {        if (cache[serviceName] === INSTANTIATING) {          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',                    serviceName + ' <- ' + path.join(' <- '));        }        return cache[serviceName];      } else {        try {          path.unshift(serviceName);          cache[serviceName] = INSTANTIATING;          return cache[serviceName] = factory(serviceName, caller);        } catch (err) {          if (cache[serviceName] === INSTANTIATING) {            delete cache[serviceName];          }          throw err;        } finally {          path.shift();        }      }    }

这里你可能会奇怪这个factory到底是哪个?是来自于providerInjector的还是来自instanceInjector的?实际上factory是由instanceInjector中定义的回调函数。为什么呢?因为上文我们说了外部调用injector的时候angular内部返回的是instanceInjector。所以在instanceInjector上调用get时,这个factory应该就是instanceInjector上注册的回调函数,来看一下这个回调:

function(serviceName, caller) {            var provider = providerInjector.get(serviceName + providerSuffix, caller);            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);          }

在instanceInjector的回调里面调用providerInjector的get,其实这个很清楚,之前angular将所有服务存储都存放在了providerCache里面,而外部对用户而言,他无法直接修改providerCache里面的东西。重复的get我们就不看,来看一下invoke方法:

function invoke(fn, self, locals, serviceName) {      if (typeof locals === 'string') {        serviceName = locals;        locals = null;      }      var args = [],          $inject = createInjector.$$annotate(fn, strictDi, serviceName),          length, i,          key;      for (i = 0, length = $inject.length; i < length; i++) {        key = $inject[i];        if (typeof key !== 'string') {          throw $injectorMinErr('itkn',                  'Incorrect injection token! Expected service name as string, got {0}', key);        }        args.push(          locals && locals.hasOwnProperty(key)          ? locals[key]          : getService(key, serviceName)        );      }      if (isArray(fn)) {        fn = fn[length];      }      // http://jsperf.com/angularjs-invoke-apply-vs-switch      // #5388      return fn.apply(self, args); //执行服务    }

千呼万唤始出来啊。服务的执行在invoke方法得到执行,那么我们算是基本的将module和服务注册和执行的流程简单的过了一遍。

简单回顾一下,angular的module实际上就是用了两层闭包来管理对象,没有动态异步加载模块,这个功能有点类似namespace。比较鸡肋。另外返回的moduleInstance中有factory,service,provider,value等一些可以注册服务的接口,其内部使用了providerInjector何instanceInjector来存储相关服务。通过invoke来执行相应模块里的相应服务。目前我们还没有看angular的bootstrap部分,其内部也是调用invoke方法,达到内置注册的服务得到执行。

时间不多,内容刚好,以上是个人阅读源码的一些理解,有不对或者偏差的地方,还希望园友们斧正。共同进步。

 
原创粉丝点击