Open hytzgroup opened 5 years ago
[TOC]
注射器负责加载模块,分析依赖,执行工厂函数,是整个AngularJS中的核心代码。一个angular应用只有一个注射器。
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>angularJS注射器解析</title> <script type="text/javascript" src="./angular.js"></script> </head> <body> <div> <button id='btnServ'>注入service</button> <button id='btnCtrl'>注入Controller</button> </div> <script type="text/javascript"> var demoApp = angular.module('demoApp',[]), btnServ = document.getElementById('btnServ'), btnCtrl = document.getElementById('btnCtrl'); demoApp.service('helloServ',[ '$window', function($window){ this.greet = function(text){ $window.alert(text); } } ]); demoApp.controller('helloCtrl',[ '$rootScope', 'helloServ', function($rootScope, helloServ){ $rootScope.name = '吴彦祖'; helloServ.greet($rootScope.name+'say: 我最帅'); } ]); // instanceInjector injector = angular.injector(['ng','demoApp']); btnServ.onclick = function(){ var helloServ = injector.get('helloServ'); helloServ.greet('hello world'); }; btnCtrl.onclick = function(){ injector.instantiate([ '$controller', function($controller){ $controller('helloCtrl') } ]); }; </script> </body> </html>
injector = angular.injector([]); log(injector) function log(injector){ var result = '', item; for(var key in injector){ item = injector[key]; if(isArray(item)){ item = "["+item.toString()+"]"; }else{ item = item.toString(); } result += (key+' : '+item+'\n'); } console.log(result) } function isArray(arr){ var type = Object.prototype.toString; return typeof arr === 'object'&&type.call(arr) === "[object Array]" }
通过控制台查看injector上的属性和方法:
{ invoke : function invoke(fn, self, locals, serviceName){ /** * 通过$$annotate方法分析出函数的依赖 * 1.声明注入 ['$rootScope', 'helloServ',fn] * 2.推断注入 function($rootScope, helloServ) * 分析出依赖之后,获取instanceInjector上的实例,如果没有调用providerInjector实例化一个实例返回 * 实例化完成,执行工厂函数 */ }, instantiate : function instantiate( Type, locals, serviceName){ /** * 支持两种类型 [] 或者 fn * 实例化该工厂函数 */ }, get : function getService(serviceName, caller) { /** * 从instanceCache中获取实例 * 若没有查找到该实例,调用providerInjector实例化 */ }, annotate : function annotate(fn, strictDi, name) { /** * 分析函数签名,获取依赖,支持两种格式 * ['$rootScope', 'helloServ',fn] * function($rootScope, helloServ) * fn[$inject] = ['$rootScope', 'helloServ'] */ }, has : function (name) { } }
//在providerCache上缓存vider providerCache = { $provide:{ provider:fn, factory:fn, service:fn, value:fn, constant:fn, decorator:decorator } }; providerInjector = providerCache.$injector = // createInternalInjector返回注射器的实例,即: // invoke、instantiate、get、annotate、has四个方法 createInternalInjector( providerCache, function(serviceName, caller){ /** * 判断providerCache有没有需要的serviceProvider * 若没有强制抛错 */ } )
通过查看createInjector的源码分析得出以下结论:
controller、service、factory等参数化处理[$provider,method,arguments]后,无论什么类型最后都会通过$provide.provider注册成一个"provider",缓存在providerCache上
providerInjector是providerCache.$injector的一个引用,providerInjector返回的是一个对象,其中getService方法的查找范围providerCache,回调函数是在没有找到需要的provider做抛错处理
providerCache.$provide源码分析
// providerCache.$provide控制台打印 { provider : function (key, value) { if (isObject(key)) { forEach(key, reverseParams(provider)); } else { return provider(key, value); } }, factory : function (key, value) {}, service : function (key, value) {}, value : function (key, valueFn) {}, constant : function (key, value) {}, decorator : function decorator(serviceName, decorFn) { // 设计模式AOP var origProvider = providerInjector.get(serviceName + providerSuffix), orig$get = origProvider.$get; origProvider.$get = function () { var origInstance = instanceInjector.invoke(orig$get, origProvider); return instanceInjector.invoke(decorFn, null, { $delegate : origInstance }); }; } } // provider源码 function provider(name, provider_) { /** * 支持三种种类型 * function xx(){ return {$get:fn||obj} } * ['$scope',function(){ return {$get:fn||obj} }] * {$get:fn||obj} */ assertNotHasOwnProperty(name, 'service'); if (isFunction(provider_) || isArray(provider_)) { // fn、[]类型需要先实例化 provider_ = providerInjector.instantiate(provider_); } // 强制要求$get属性 if (!provider_.$get) { throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); } // 注册到providerCache return providerCache[name + providerSuffix] = provider_; }
// instanceCache缓存provider生成的实例 instanceCache = {}; instanceInjector = instanceCache.$injector = createInternalInjector( providerCache, function(serviceName, caller){ /** * 从providerCache上获取provider * instanceInjector初始化获取的provider */ var provider = providerInjector.get(serviceName + providerSuffix, caller); return instanceInjector.invoke(provider.$get, provider, undefined, serviceName); } )
通过查看createInjector的源码可以得出以下结论
在创建完providerInjector和instanceInjector两个对象后,loadModules开始加载定义好的模块[ 'ng', ['$provide', function($provide) { $provide.value('$rootElement', element);} ], 'demoApp']
[ 'ng', ['$provide', function($provide) { $provide.value('$rootElement', element);} ], 'demoApp']
// ng模块参数化处理后 { name:'ng', requires:['ngLocale'], _configBlocks:[ ['$injector', 'invoke', ['$provide',function ngModule] ] ], _invokeQueue:[], _runBlocks:[], ... } // ngLocale模块参数化处理后 { name:'ngLocale', requires:[], _configBlocks:[ ['$injector', 'invoke', ['$provide',fn] ] ], _invokeQueue:[], _runBlocks:[], ... } // demoApp模块参数化处理后 { name:'demoApp', requires:[], _configBlocks:[], _invokeQueue:[ ["$provide", "service", ['helloServ', ["$window", ƒn]] ], ["$controllerProvider", "register", ['helloServ', ["$rootScope", "helloServ", ƒn] ] ], ], _runBlocks:[], ... }
loadModules依次递归加载模块,源码如下:
function loadModules(modulesToLoad) { /** * String类型的模块 * 递归分析依赖,添加到runBlocks中。在providerCache中查找provider(invokeArgs[0]),执行provider对应的方法(invokeArgs[1])并传参数(invokeArgs[2]) * Function类型 / Array类型 * 实例化模块,添加到runBlock上 */ var runBlocks = [], moduleFn; forEach(modulesToLoad, function (module) { 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; }
注射器是angularJS中的核心代码,一个angularJS应用只有一个注射器,注射器内部会创建两个对象,分别为:providerCache、instanceCache。providerCache缓存provider函数,instanceCache缓存实例。providerCache、instanceCache都有一个$injector注射器,providerCache.$injector主要是用来分析出构造函数遇到[''],然后缓存,instanceCache.$injector主要是负责实例化
<!DOCTYPE html>
AngularJS注射器解析
[TOC]
概述
注射器负责加载模块,分析依赖,执行工厂函数,是整个AngularJS中的核心代码。一个angular应用只有一个注射器。
实例
注射器返回值分析
通过控制台查看injector上的属性和方法:
providerCache和instanceCache的区别
providerCache分析
通过查看createInjector的源码分析得出以下结论:
controller、service、factory等参数化处理[$provider,method,arguments]后,无论什么类型最后都会通过$provide.provider注册成一个"provider",缓存在providerCache上
providerInjector是providerCache.$injector的一个引用,providerInjector返回的是一个对象,其中getService方法的查找范围providerCache,回调函数是在没有找到需要的provider做抛错处理
providerCache.$provide源码分析
instanceCache分析
通过查看createInjector的源码可以得出以下结论
loadModules分析
在创建完providerInjector和instanceInjector两个对象后,loadModules开始加载定义好的模块
[ 'ng', ['$provide', function($provide) { $provide.value('$rootElement', element);} ], 'demoApp']
loadModules依次递归加载模块,源码如下:
总结
注射器是angularJS中的核心代码,一个angularJS应用只有一个注射器,注射器内部会创建两个对象,分别为:providerCache、instanceCache。providerCache缓存provider函数,instanceCache缓存实例。providerCache、instanceCache都有一个$injector注射器,providerCache.$injector主要是用来分析出构造函数遇到[''],然后缓存,instanceCache.$injector主要是负责实例化