angluar浅析1 module和injection

本系列为AngularJS v1.3.20版本
我们知道在angluar在运行过程中, module和injection是它的基本组织架构。 因此做我们不妨以此处作为入口进行分析。
一个简单的示例如下
通过angular.module模块创建一个module对象, 在该对象上设置一个常量name, 一个服务info。
通过injector创建一个注射器, 使用get获取到module上注册的服务。

function test1() {
    const module = window.angular.module('myModule', []);
    module.constant('name', 'ali')
    module.provider('info', {
        $get: function (name) {
            return {
                name: name + Date.now(),
                age: 18
            }
        }
    });

    const injector = angular.injector(['myModule'])
    console.log(injector.get('info'))
}

setupMouleLoader

在上述示例中我们发现模块创建方法是挂载到angular上的, 所以angular.module('myModule', [])调用之前会有一系列的初始化工作。
而setupMouleLoader即是用来创建一个模块加载器.
一下代码主要功能如下

  1. 创建angular对象,且在该对象上挂载用于创建模块的module方法
  2. 通过module方法创建moduleInstance实例, 该实例上挂载有provider、factory、service、constant等方法。
  3. 有代码可知invokeLater是一个延迟方法, 也就是说调用provider或者constant方法并不会将对应的服务和常量挂载到module上,
    而是要等到loadModules阶段
function setupModuleLoader(window) {

  var $injectorMinErr = minErr('$injector');
  var ngMinErr = minErr('ng');
 
  function ensure(obj, name, factory) {
    return obj[name] || (obj[name] = factory());
  }
  
  // 1. 创建一个angluar对象
  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() {
    /** @type {Object.} */
    var modules = {};

     // 2, 创建模块调用方法
    return function module(name, requires, configFn) {
      return ensure(modules, name, function() {

        /** @type {!Array.>} */
        var invokeQueue = [];

        /** @type {!Array.} */
        var configBlocks = [];

        /** @type {!Array.} */
        var runBlocks = [];

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

        /** @type {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'),
          ...
          run: function(block) {
            runBlocks.push(block);
            return this;
          }
        };

        if (configFn) {
          config(configFn);
        }

        return moduleInstance;
        // 注册服务延迟执行, 
        function invokeLater(provider, method, insertMethod, queue) {
          if (!queue) queue = invokeQueue;
          return function() {
            queue[insertMethod || 'push']([provider, method, arguments]);
            return moduleInstance;
          };
        }
      });
    };
  });

}

createInjector

该方法用来创建注射器,返回一个injector实例

  1. 使用providerCache、instanceCache进行缓存设置。
  2. providerInjector是一个injector实例, providerCache用来存储原始的服务对象, 在loadModules阶段将服务进行缓存。
  3. instanceInjector也是一个injector实例对象,instanceCache用来存储服务$get结果对象 。providerInjector是存在于闭包内部的私有变量, 而instanceInjector则是return到外部的实例方法。
    在AngularJS如果我们需要提供依赖注入服务时,会先在instanceCache查询是否存在该服务,如果不存在则转交给providerInjector进行查询(惰性加载的思想: 第一次访问某个服务时instanceCache并不存在, 这时候会从providerInjector中获取,并通过invoke方法触发$get调用返回调用结果)
function createInjector(modulesToLoad, strictDi) {
  strictDi = (strictDi === true);
  var INSTANTIATING = {},
      providerSuffix = 'Provider',
      path = [],
      loadedModules = new HashMap([], true),
      providerCache = {
        $provide: {
            ...
          }
      },
      providerInjector = (providerCache.$injector =
          createInternalInjector(providerCache, function(serviceName, caller) {
            if (angular.isString(caller)) {
              path.push(caller);
            }
            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
          })),
      instanceCache = {},
      instanceInjector = (instanceCache.$injector =
          createInternalInjector(instanceCache, function(serviceName, caller) {
              // 如果该injector实例中不存在则去providerInjector中获取, 执行$get返回值作为依赖服务, 这也是在创建provider的时候需要$get的原因
            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;

  ////////////////////////////////////
  // $provider
  ////////////////////////////////////

  ...

  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 constant(name, value) {
    assertNotHasOwnProperty(name, 'constant');
    providerCache[name] = value;
    instanceCache[name] = value;
  }

  ...
  
}

loadModules

该方法主要功能:遍历模块对象, 对之前存储在_invokeQueue中的待挂载的服务依次存储在providerCache缓存中。

function loadModules(modulesToLoad) {
    var runBlocks = [], moduleFn;
    forEach(modulesToLoad, function(module) {
      if (loadedModules.get(module)) return;
      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);
          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
          runInvokeQueue(moduleFn._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) {
        ...
      }
    });
    return runBlocks;
  }

  

createInternalInjector

该方法的功能为

  1. 一个getService方法用来获取服务
  2. 一个invoke方法用来执行原服务的$get方法,返回真正的服务对象
  3. 一个instantiate方法用来实例化一个服务
  4. 最终返回injector实例对象。
////////////////////////////////////
  // internal Injector
  ////////////////////////////////////

  function createInternalInjector(cache, factory) {

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

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

      // http://jsperf.com/angularjs-invoke-apply-vs-switch
      // #5388
      return fn.apply(self, args);
    }

    function instantiate(Type, locals, serviceName) {
      // Check if Type is annotated and use just the given function at n-1 as parameter
      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
      // Object creation: http://jsperf.com/create-constructor/2
      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
      var returnedValue = invoke(Type, instance, locals, serviceName);

      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
    }

    return {
      invoke: invoke,
      instantiate: instantiate,
      get: getService,
      annotate: createInjector.$$annotate,
      has: function(name) {
        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
      }
    };
  }

provider、 factory、service

有代码可知factory只是用provider方法进行包装
而service在factory包装的基础上通过 $injector.instantiate返回一个实例话对象

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, {
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
  }

  function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
  }

你可能感兴趣的:(angluar浅析1 module和injection)