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)
- Angular源码解读的setupModuleLoader函数
- Angularjs 源码分析-setupModuleLoader
- angular源码解读:$apply方法
- Angular源码解读之Render
- angular源码解读:forEach方法遍历
- packageInstalled函数的第二个参数returnCode的源码解读
- C函数源码解读:atof
- Nginx源码main函数解读
- Nginx源码main函数解读
- angular的$watch 函数
- OpenCV的GrabCut函数使用和源码解读
- OpenCV的GrabCut函数使用和源码解读
- 关于cocos2dx lua中的clone函数的源码解读
- XE8-indy10中TIdThread.Execute函数的源码与解读
- OpenCV的GrabCut函数使用和源码解读
- SSD的源码解读——MultiBoxLoss函数定义
- struts2源码的解读
- struts2源码的解读 .
- 九度 OJ 1031:xxx定律
- iOpenWorksSDK下载地址
- JAVA 线程和进程之间的关系
- 不定高多行溢出文本省略
- 第十一周程序阅读
- Angular源码解读的setupModuleLoader函数
- js常见事件及案例-onSubmit
- Android 异步网络请求导致的程序崩溃
- 文件拷贝速度比较
- 3d基础图形原理
- phaser.js显示对象篇
- The APK file does not exist on disk
- spring整合mybatis
- Retrofit2 使用经验