美文网首页angular系列
angluar浅析1 module和injection

angluar浅析1 module和injection

作者: 百里哈哈 | 来源:发表于2020-09-11 08:50 被阅读0次

    本系列为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.<string, angular.Module>} */
        var modules = {};
    
         // 2, 创建模块调用方法
        return function module(name, requires, configFn) {
          return ensure(modules, name, function() {
    
            /** @type {!Array.<Array.<*>>} */
            var invokeQueue = [];
    
            /** @type {!Array.<Function>} */
            var configBlocks = [];
    
            /** @type {!Array.<Function>} */
            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

        本文链接:https://www.haomeiwen.com/subject/wulvektx.html