module模块 - acelan86/angular GitHub Wiki

angular.module哪来的?

看下面的代码:

publishExternalAPI(angular); //加载angular时调用#14482


//publishExternalAPI定义 #1346
function publishExternalAPI(angular){
    ...
    angularModule = setupModuleLoader(window);
    ...
}


/**
 * setupModuleLoader定义 #1079
 * 这个方法起始就是达到下面的效果:
 *  1. window.anguler.module = function module(name, require, configFn) {};
 *  2. 闭包了一个modules用来缓存已经初始过的模块
 */
function setupModuleLoader(window) {
    ...
     return ensure(ensure(window, 'angular', Object), 'module', function() {
        var modules = {}; //用来缓存已经加载的模块
        ...
        return function module(name, requires, configFn) {
             //这里为了结构方便看,内容拉到后面去解释
        };
    });

}

/**
 * 主角来了,API:anguler.module方法
 * 它做了下面几点:
 * 1. 如果有同名不同依赖的模块声明,则重新定义一个新模块,放弃原有那个
 * 2. module[name] = moduleInstance
 * 3. 处理configFn
 * 4. return module[name], 就是本次生成的moduleInstance
 */
function module(name, requires, configFn) {
  //#1
  if (requires && modules.hasOwnProperty(name)) {
     modules[name] = null;
  }
  
  //#2,3,4
  return ensure(modules, name, function() {
      //不是用function ModuleInstance来声明对象,而是用一个对象字面量来处理 
      var moduleInstance = {
         //moduleInstance的属性及方法见后面定义...
      };

      //主调用对象为$injector,调用方法是invoke,用来获取依赖注入,并执行方法。区别其他provider,如moduleInstance的provider,factory,controller等,他们的主调用对象是$provider。
      var config = invokeLater('$injector', 'invoke'); 
      if (configFn) {
          config(configFn); //module方法的第三个参数,如有,则将方法注入invokeQueue,在loadModule的时候执行
      }
      return moduleInstance;

      /*
        每个module实例方法基本上都是调用这货来实现的
        顾名思义,延后调用,那延后到什么时候调用?先放着吧。。
        这里的用处就是往moduleInstance的_invokeQueue里面push进我要延后做的方法(记录了方法提供者,方法名,参数)。
        最后返回这个moduleInstance实现链式调用
      */
      function invokeLater(provider, method, insertMethod) {
          return function() {
              invokeQueue[insertMethod || 'push']([provider, method, arguments]);
              return moduleInstance;
          }
      }
  });
}

//moduleInstance
var moduleInstance = {
    _invokeQueue: invokeQueue,  //调用队列,createInjector时调用的loadModules方法中使用
    _runBlocks: runBlocks,      //这货反正现在看不出干嘛
    requires: requires,         //模块依赖没有被处理,直接放入module的实例属性中
    name: name,
    //各种在module实例化后可以执行的接口方法在这里声明
    provider: invokeLater('$provide', 'provider'),
    factory: invokeLater('$provide', 'factory'),
    service: invokeLater('$provide', 'service'),
    value: invokeLater('$provide', 'value'),
    constant: invokeLater('$provide', 'constant', 'unshift'),
    filter: invokeLater('$filterProvider', 'register'),
    controller: invokeLater('$controllerProvider', 'register'),
    directive: invokeLater('$compileProvider', 'directive'),
    config: config, //invokeLater('$injector', 'invoke'),
    run: function(block) {
      runBlocks.push(block);
      return this;
    }
};

声明module和在module上声明controller

理解了上面的来源,接下来,写我们的angular程序,从app.js入手

angular.module('yeomanApp', [])
...

按上面的module function的分析,这时候结果是闭包的modules缓存里面加入了modules['yeomanApp'] = moduleInstance;

然后我们往下走,再module上定义一个controller:

angular.module('yeomanApp', [])
    .controller('UserCtrl', ['$scope', function ($scope) {
        console.log($scope);
    }]);

即调用模块声明后生成的moduleInstance的controller方法, 即: 执行invokeLater('$controllerProvider', 'register')('UserCtrl', ....), 即完成下面两个步骤:

  • moduleInstance._invokeQueue.push('$controllerProvider', 'register', ['UserCtrl', ...])
  • 返回moduleInstance供链式调用

注意,这里仅仅是将生成controller的controllerProvider的register方法写到module的调用队列(invokeQueue)中,没有做其他更多的操作。