您的位置:首页 > 产品设计 > 产品经理

Angular源码解读的setupModuleLoader函数

2016-05-25 18:02 591 查看
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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  angular 源码