Angular源码解读的setupModuleLoader函数

来源:互联网 发布:js ui隐藏不安全 编辑:程序博客网 时间:2024/05/16 04:03

setupModuleLoader其实看字面意思就可以知道它为模板加载器,就是为module设置加载器
function setupModuleLoader(window) {}
首先传入了window对象,作用为为后面ensure判断window全局对象是否含有属性angular

function ensure(obj, name, factory) {            return obj[name] || (obj[name] = factory())        }

这里有个闭包函数,作用是判断对象中是否存在某个属性,没有的话创建对应的方法,并执行该方法
所以下面就会看到
ensure(ensure(window, "angular", Object), "module", function() {})
第一个ensure创建了为window对象创建angular
window.angular = Object()
第二个ensure创建了为angular对象创建module
到此步往下再看其实就变成了这样了
angular.module = function module(name,require,configFn);
- name:字符串类型,代表模块的名称;
- requires:字符串的数组,代表该模块依赖的其他模块列表,如果不依赖其他模块,用空数组即可;
- configFn:用来对该模块进行一些配置。

这里就是我们常用到的模块加载部分
当我们angular.module(‘wscatApp’),只传一个参数,为getter操作,返回moduleInstance
当我们angular.module(‘wscatApp’,[]) ,传了requires数组(空数组也行),为setter操作,也是返回对象moduleInstance
这两者有什么不同呢,可以看到module方法可以传递三个参数第二个参数require就是我们需要注入的依赖数组,其实就是注入所谓的DI

什么是DI呢,DI就是依赖注入
详情可以看这篇文章阐述
Javascript DI!Angular依赖注入的实现原理
截取里面几句话,方便这里的理解

  • 根据DI的原理,一个自然的推论就是:被注入的对象通常都是单例,因为创建了一个,就可以始终使用它了,不需要多次创建。
  • 在Angular中,所有主要编程元素都需要通过某种方式注册进去,比如myModule.service(‘serviceName’, function()….这实际上就是把后面这个函数加入到一个容器中,要注意的是:angular全面实现了延迟初始化,也就是说,当这个对象没有被别人需要的时候,它是不会被创建的,这样对于提高性能有一定的帮助,特别是加快了启动速度。

这里的模块包括控制器、服务、过滤器、指令等子元素组成的整体
具体看源码这部分

moduleInstance = {                            _invokeQueue: invokeQueue,                            _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) {                                return runBlocks.push(block), this                            }                        };

再往下看,里面有个闭包函数

var assertNotHasOwnProperty = function(name, context) {                    if ("hasOwnProperty" === name) throw ngMinErr("badname", "hasOwnProperty is not a valid {0} name", context)                };

这句也挺可爱的,module名称是不能以hasOwnProperty命名,否则会抛出”badname“的错误提醒。
下面继续执行
assertNotHasOwnProperty(name, "module"), requires && modules.hasOwnProperty(name) && (modules[name] = null), ensure(modules, name, function() {})
由于这里有多个异或操作,其实简单看起来就是进行了如下操作

assertNotHasOwnProperty(name, 'module');      if (requires && modules.hasOwnProperty(name)) {        modules[name] = null;      }      return ensure(modules, name, function() {})

这里很容易看出来,如果有传入requires数组(就算是空数组)还有modules对象里面有已定义好的对象或者属性,则会删除已有的module信息,将其置为null,并重新定义该属性

这里注意的是对比的modules对象,是在这个里面的
var modules = {};
它并不是全局的,而是只是在闭包function(name, requires, configFn){}局部作用域的。但由于是在闭包里面,所以它会一直保存在作用域中,相当于一直被缓存,不会被释放掉。
继续往下面看
if (!requires) throw $injectorMinErr("nomod", "Module '{0}' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.", name);
而requires如果为空,则表示是获取已有的模块
但如果requires不是空而是根本不存在,也就是说没有定义过这个module

看到上面这部分源码后启示我们在创建模块时,就算没有依赖其他模块,写法也应该是:
angular.module('wscatApp', []);
否则会到这里报错
angular.module(‘wscatApp’,[…])和angular.module(‘wscatApp’)
区别在于
- angular.module(‘wscatApp’,[…])会创建一个新的Angular模块,然后把方括号([…])中的依赖列表加载进来;
- 而angular.module(‘wscatApp’)会使用由第一个调用定义的现有的模块。

到这一步就是介绍这部分的最后一个闭包函数

function invokeLater(provider, method, insertMethod) {                        return function() {                            return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance                        }                    }

invokeLater从字面意思不难看出是之后再调用
理解这里我决定用一段简单的代码来阐述

var app = angular.module('wsscat', []);        app.controller('serCtrl', function($scope, cat) {            $scope.arr = cat.arr        })

实际上我们还可以这样写

var app = angular.module('wsscat', []);app.controller('serCtrl', ['$scope', 'cat', function($scope, cat) {            //$scope.arr = cat.arr            $scope.arr = "wsscat"        }])

app.controller('serCtrl', function($scope, cat) {})
的controller运行了moduleInstance对象中的的invokeLater函数
controller: invokeLater("$controllerProvider", "register")
invokeLater传递三个参数

  • 第一个我们传入了$controllerProvider对应了provider
  • 第二个我们传入了register对应了method
  • 第三个我们传入了’serCtrl’, function(scope, cat) {scope.arr = cat.arr}对应了arguments

所以实际上变成了这样invokeQueue.push()

invokeQueue.push([$controllerProvider,register,['$scope', 'cat', function($scope, cat) {            //$scope.arr = cat.arr            $scope.arr = "wsscat"        }]])

invokeQueue[insertMethod || "push"]([provider, method, arguments])
invokeQueue默认队列操作是push,如果有传入其他insertMethod
constant: invokeLater("$provide", "constant", "unshift")
则是unshift操作
animation: invokeLater("$animateProvider", "register")
则是register操作
由于return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance最后执行队列存放之后,会返回invokeQueue,所以后面可以进行链式操作,例如这样
app.controller().factory()

其他的模块和实例如下
举一反三,这里就不继续展开写了,引入AngularJS模块详解写的列表,如下

  • requires
  • 表示模块的依赖数组,即angular.module方法的requires参数。

angular模块实例属性和方法
属性
name
模块的名字。

  • _invokeQueue、_configBlocks、_runBlocks
  • 分别对应invokeQueue、configBlocks、runBlocks。

方法
模块的以下方法最后全部会返回模块实例本身,形成执行链。

  • animation()
  • 调用这个方法表示这个模块将在$animateProvider中注册一个动画服务。
  • 原理:在invokeQueue尾部插入[‘$animateProvider’, ‘register’, arguments]。
  • config()
  • 调用这个方法,表示给这个模块追加一个配置方法。
  • 原理:在configBlocks尾部插入[‘$injector’,’invoke’, arguments]。
  • constant()
  • 调用这个方法表示这个模块将给默认的$provider注册一个常量。
  • 原理:在invokeQueue首部插入[‘$provide’, ‘constant’, arguments]。
  • controller()
  • 调用这个方法表示模块将在$controllerProvider中注册一个控制器。
  • 原理:在invokeQueue尾部插入[‘$controllerProvider’, ‘register’, arguments]。
  • directive()
  • 调用这个方法表示这个模块将在$compileProvider中注册一个指令。
  • 原理:在invokeQueue尾部插入[‘$compileProvider’, ‘directive’, arguments]。
  • factory()
  • 调用这个方法表示这个模块中将生成一个服务工厂(隐式创建一个了服务提供商)。
  • 原理:在invokeQueue尾部插入[‘$provide’, ‘factory’, arguments]。
  • filter()
  • 调用这个方法表示这个模块将在$filterProvider中注册一个过滤器。
  • 原理:在invokeQueue尾部插入[‘$filterProvider’, ‘register’, arguments]。
  • provider()
  • 调用这个方法表示这个模块将添加一个服务提供商。
  • 原理:在invokeQueue尾部插入[‘$provide’, ‘provider’, arguments]。
  • run(block)
  • 调用这个方法表示这个模块将执行某个功能块,block可以是方法,也可以是数组。
  • 原理:在invokeQueue尾部插入block。
  • service()
  • 调用这个方法表示这个模块将注册一个服务(隐式创建了一个服务提供商)。
  • 原理:在invokeQueue尾部插入[‘$provide’, ‘service’, arguments]。
  • value()
  • 调用这个方法表示这个模块将注册一个变量(隐式创建了一个服务提供商)。

看最后一句
return configFn && config(configFn), moduleInstance
这句转化成这样容易点理解

if (configFn) {          config(configFn);        }

也就是如果声明module时存在第三个参数,那么就把第三个参数的设置放进invokeQueue队列中。这样,就可以在载入一个module的时候同时做一些其他的事情。
config = invokeLater("$injector", "invoke")
config(configFn)
invokeLater("$injector", "invoke")(configFn)

2 0