摘要:同名模塊已經初始化的模塊保存在一個叫的緩存對象中,是模塊名,是模塊對象。調用注入器的方法執行模塊的所有方法。檢查該注入器中是否存在指定的服務。如果是數組,最后一個必須是的構造函數,前面的就是構造函數的參數名。
模塊
模塊是指寫Angular應用的代碼片段,這樣可以使代碼分離開來,因此代碼會更好維護,可讀和測試。還可以在module里定義代碼依賴關系,可以調用一個模塊,再在代碼中定義這個模塊依賴于另外兩個。
angular模塊通過angular.module(name,requires, configFn)方法生成:
1>參數name是模塊名稱;
2>參數requires表示依賴模塊數組。
如果不設置requires參數,調用angular.module(name)方法表示獲取這個模塊;如果確定新模塊沒有依賴關系,必須設置requires為空數[arguments];
如果不是字符串,則必須是方法(或數組格式的方法),那么,這個方法就代表了一個模塊。
3>參數configFn是方法或數組,負責在模塊初始化時做一些配置,如果是數組,最后一個元素必須是方法。
方法configFn并不是在執行angular.module()的時候立即執行,而是當這個模塊被第一次使用時,由注入器調用執行。同時,查看方法configFn中的this就會發現,這個this在瀏覽器中指向的是window,而不是module。而且,方法configFn只會執行一次,因此同一個angular模塊不會重復配置。
實例:
angular.module("MetronicApp") .controller("AppController", ["$scope", "$rootScope", "AppCommonService", "DictService", function ($scope, $rootScope, AppCommonService, DictService) { // code }); }]);
AngularJS允許我們使用angular.module()方法來聲明模塊,這個方法能夠接受兩個參數, 第一個是模塊的名稱,第二個是依賴列表,也就是可以被注入到模塊中的對象列表。
同名模塊
已經初始化的angular模塊保存在一個叫modules的緩存對象中,key是模塊名,value是模塊對象。所以,定義一個同名的模塊,等于覆蓋之前的模塊。
服務注入angular模塊只保留服務的定義,現在再來理解服務是如何加入注入器的。
模塊定義服務、服務提供商; 注入器根據模塊依賴關系加載模塊,實例化所有服務提供商; 應用需要服務,注入器根據服務名尋找服務提供商,服務提供商實例化服務。
以上只是理論,現在從代碼層面來看Angular是如何實現的。
每個angular模塊內置有三個數組,invokeQueue保存如何注入服務提供商和值的信息;configBlocks保存模塊的配置信息;runBlocks保存這個模塊的執行信息。模塊被使用的時候,注入器根據invokeQueue中的信息,實例化服務提供商;根據configBlocks中的信息對服務提供商做一些額外的處理;根據runBlocks中提供的信息,調用前面的服務提供商提供的服務執行模塊需要完成的工作。
angular模塊提供了很多方法來填充這三個數組,比如config()、run()等。三個數組的元素也是數組,具體元素格式參考后面的說明。
以下是源碼,可以清楚看到每個模塊所要依賴的服務和運行機制,稍后詳細介紹。
var angular = ensure(window, "angular", Object); angular.$$minErr = angular.$$minErr || minErr; return ensure(angular, "module", function() { var modules = {}; return function module(name, requires, configFn) { var assertNotHasOwnProperty = function(name, context) { if (name === "hasOwnProperty") { throw ngMinErr("badname", "hasOwnProperty is not a valid {0} name", context); } }; assertNotHasOwnProperty(name, "module"); if (requires && modules.hasOwnProperty(name)) { modules[name] = null; } return ensure(modules, name, function() { 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); } /** @type {!Array.>} */ var invokeQueue = []; /** @type {!Array. } */ var configBlocks = []; /** @type {!Array. } */ var runBlocks = []; var config = invokeLater("$injector", "invoke", "push", configBlocks); /** @type {angular.Module} */ var moduleInstance = { // Private state _invokeQueue: invokeQueue, _configBlocks: configBlocks, _runBlocks: runBlocks, requires: requires, name: name, provider: invokeLaterAndSetModuleName("$provide", "provider"), factory: invokeLaterAndSetModuleName("$provide", "factory"), service: invokeLaterAndSetModuleName("$provide", "service"), value: invokeLater("$provide", "value"), constant: invokeLater("$provide", "constant", "unshift"), decorator: invokeLaterAndSetModuleName("$provide", "decorator"), animation: invokeLaterAndSetModuleName("$animateProvider", "register"), filter: invokeLaterAndSetModuleName("$filterProvider", "register"), controller: invokeLaterAndSetModuleName("$controllerProvider", "register"), directive: invokeLaterAndSetModuleName("$compileProvider", "directive"), config: config, run: function(block) { runBlocks.push(block); return this; } }; if (configFn) { config(configFn); } return moduleInstance; function invokeLater(provider, method, insertMethod, queue) { if (!queue) queue = invokeQueue; return function() { queue[insertMethod || "push"]([provider, method, arguments]); return moduleInstance; }; } function invokeLaterAndSetModuleName(provider, method) { return function(recipeName, factoryFunction) { if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name; invokeQueue.push([provider, method, arguments]); return moduleInstance; }; } }); };
});
調用隊列 – invokeQueue數組元素為[‘provider’, ‘method’, arguments]。
舉例– 添加一個Controller:
angular.module("ngAppDemo",[]) .controller("ngAppDemoController",function($scope) { $scope.a= 1; $scope.b = 2; });
這段代碼等于:
invokeQueue.push(["$controllerProvider","register", ["ngAppDemoController", function(){ //code }]]);
注入器根據這個信息,就會調用$controllerProvider的register方法注冊一個ngAppDemoController。
配置隊列 – configBlocks元素格式為["$injector", "invoke", arguments]。
運行隊列 – runBlocks元素要求是方法,或者是數組,數組最后一個元素是方法。
angular模塊實例屬性和方法屬性
requires
表示模塊的依賴數組,即angular.module方法的requires參數。
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()服務注入器(Service Injector) & 服務提供商(Service Provider)
調用這個方法表示這個模塊將注冊一個變量(隱式創建了一個服務提供商)。
原理:在invokeQueue尾部插入["$provide", "value", arguments]。
在Angular中,服務可能是對象、方法、或者一個常量值。服務由服務提供商創建,而服務提供商由注入器統一管理。當我們需要某個服務的時候,注入器負責根據服務名尋找相應的服務提供商,然后由服務提供商的$get()生產工廠創建服務實例。因此,服務提供商必須有一個$get()方法,這個方法就是服務創建單例工廠。
背后原理:注入器中的Providers和Services各自通過一個Map對象保存在緩存(分別對應providerCache和instanceCache)中,只不過Providers的key是serviceName + “Provider”,而Services的key是serviceName。
服務提供商-Provider
Provider即服務提供商,必須有一個$get()方法,$get()的返回值是Provider在注入器中實際的服務。
注入器
創建注入器的方法只在bootstrap()方法中被調用過,也就是說,每一個angular應用對應一個注入器。
注入過程
注入器由angular.injector(modulesToLoad, isStrictDi)方法創建,在angular中其實為createInjector方法。參數modulesToLoad是數組,元素格式為以下之一:
‘module’,模塊的名稱。 [‘service1’, ‘service2’, fn]。 fn,方法的返回值必須仍然是方法。
方法angular.injector()的執行過程:
遍歷modulesToLoad,根據moduleName尋找或生成相應的模塊。
調用模塊invokeQueue中的所有方法,這一步的目的是創建必需的服務。
調用模塊configBlocks中的所有方法,目的是用已有的服務配置這個模塊。
調用注入器的invoke()方法執行模塊runBlocks的所有方法。
返回服務注入器實例。
不是所有模塊都是對象,如果模塊本身是方法或者是數組(最后一個元素必須是方法),則運行這個方法、或數組的最后一個方法,相當于直接進入了第四步。
注入器與bootstrap的關系
創建注入器的方法在angular.js中只在bootstrap()方法中被調用過,也就是說,每一個angular應用對應一個注入器。
注入器方法
在providerCache中和instanceCache中分別內置有一個$injector對象,分別負責給模塊注入服務提供商和為方法注入服務。一般我們只談論instanceCache中的$injector對象,因為providerCache和它的$injector是私有的,只在Angular內部代碼使用。
比如,執行模塊調用隊列、配置隊列中的方法時注入的是服務提供商,而當調用運行隊列中的方法時,注入的是服務。
$injector.invoke(fn, self, locals, serviceName)
執行方法fn。locals是可選參數,是對象,表示局部變量。self是fn中的this。
最后一個參數serviceName是可選參數,表示在哪個服務中調用了fn方法,用于錯誤信息顯示,沒有處理邏輯。
$injector.instantiate(Type, locals, serviceName)
l 如果參數Type為方法,根據Type的prototype創建一個實例(通過Object.create方法創建),如果Type是數組,使用最后一個元素的prototype。
l 參數Locals是當Type方法的參數出現在locals對象中的時候,取locals[arg]的值重新作為Type的參數。如果locals中沒有,則等價于調用get(arg,serviceName)獲取service作為新的參數。
實例化過程可以簡單概括為
Type.apply(Object.create(Type.prototype),locals[argName]|| get(argName, serviceName))。
注意:實例化出來的不一定是對象,也可能是方法。
最后一個參數serviceName是可選參數,表示在哪個服務中實例化了Type,用于錯誤信息顯示,沒有處理邏輯。
$injector.get(name, caller)
從注入器中獲取一個服務實例。
參數name是服務的名稱。參數caller也是字符串,表示調用這個服務的是哪個方法,用于錯誤信息提示,沒有處理邏輯。
$injector.annotate(fn,strictDi )
返回數組,數組的元素是fn方法需要注入的依賴服務。
在嚴格模式下,方法的依賴注入必須使用顯示的注解加入,也就是說通過fn.$injector能夠獲取這個方法的依賴注入。
參數name是可選的,用于錯誤顯示,沒有處理邏輯。
方法annotate()也可以接受數組,數組的最后一個參數一定是fn,前面的元素則是依賴。
$injector.has(name)
檢查該注入器中是否存在指定的服務。
Provider方法
注入器的providerCache中內置有一個$provider對象,這是注入器的默認服務提供商,$provider有六個固定的方法。這幾個方法的作用主要是為注入器添加其他服務提供商。
注意:
以下所有方法的name參數不需要以“Provider”結尾,因為provider()方法會默認把這個后綴加上。 以下任何一個方法不做同名判斷,因此,如果出現同名,后者將覆蓋前者。
$provider.provide(name, provider)
參數provider可以是方法或數組,也可以是對象。
l 如果是方法,則是provider的構造函數。調用注入器的instantiate()方法,生成一個provider實例,并以name為key保存在注入器的providerCache中。
l 如果是數組,最后一個必須是provider的構造函數,前面的就是構造函數的參數名。之后的原理和provider是方法的情形相同。
l 如果是對象,說明這個provider已經被實例化了,只需有$get()方法即可。
$provider.factory(name, factoryFn, enforce)
使用$provider.provide()一般需要定義一個Provider類,如果不想定義Provider類,而是直接定義服務工廠,就可以使用這個方法。
背后原理:首先生成一個匿名對象,這個對象的$get屬性就是factoryFn(enforce為false的情況下),然后把這個匿名對象作為$provider.provide()方法的第二個參數。所以,factoryFn其實依然是綁定在一個provider上的。
$provider.service(name, constructor)
調用injector.instantiate()方法,利用參數constructor生成service實例,參數name是這個service的名稱。
眾所周知,service由provider提供,那這個方法是怎么回事?原理:Angular首先根據constructor生成一個factoryFn,然后調用$provider.factory(name, factoryFn)。所以其實還是生成了一個provider。
舉例:
$provider.service("filter", constructor)
等于創建了一個filter服務實例,并且在providerCache中保存了一個名稱為“filterProvider”的服務提供商。
$provider.value(name, value)
這個方法實際調用injector.factory(name,valueFn(value), false)方法實現。所以其實等于創建一個只提供值的服務提供商。
$provider.constant(name, value)
這個方法直接在providerCache中添加一個屬性實現。
$provider.decorate(serviceName, decorFn)
修改舊的服務,改為執行decorFn方法,并把servcieName原來的服務作為一個參數,參數名為$delegate。等價于一個靜態代理。
背后原理:首先根據seviceName找到Provider,然后修改provider的$get屬性。
angular內置模塊ngLocale - 本地化模塊
angular.module("ngLocale",[]).provider("$locale", $LocaleProvider);
結果是invokeQueue.push(["$provide", "provider",["$locale", $LocaleProvider]]);
ng
angular.module("ng",["ngLocale"]).config(["$provide", function(){}]);
結果是configBlocks.push(["$injector", "invoke",["$provide", function(){}]])。
三個固定模塊
每個使用bootstrap(element, modules, config)生成的應用,注入器中有三個固定的模塊:
第一個模塊是"ng"。
第二個模塊是[‘$provider’,fn],它的作用是把根元素element作為變量保存在$provider中。
第三個模塊是[‘$compileProvider’,fn],它的作用是根據config.debugInfoEnabled調用 $conpileProvider.debugInfoEnabled(true)。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/92593.html
摘要:入門一前言目前來說相對于現在流行的高版本以及來說實屬是老套的前端框架了,當然這都不重要,沒有完美的框架,只有不斷優化的代碼。通過使用我們稱為指令的結構,讓瀏覽器能夠識別新的語法。使用作為輸入,而不是字符串,是區別于其它的框架的最大原因。 AngularJs 入門(一) 前言 AngularJs目前來說相對于現在流行的高版本ng2、ng4,以及Vue2.0、React來說實屬是老套的前...
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
摘要:如何在中使用動畫前端掘金本文講一下中動畫應用的部分。與的快速入門指南推薦前端掘金是非常棒的框架,能夠創建功能強大,動態功能的。自發布以來,已經廣泛應用于開發中。 如何在 Angular 中使用動畫 - 前端 - 掘金本文講一下Angular中動畫應用的部分。 首先,Angular本生不提供動畫機制,需要在項目中加入Angular插件模塊ngAnimate才能完成Angular的動畫機制...
閱讀 1787·2021-11-25 09:43
閱讀 15421·2021-09-22 15:11
閱讀 2632·2019-08-30 13:19
閱讀 2015·2019-08-30 12:54
閱讀 1821·2019-08-29 13:06
閱讀 930·2019-08-26 14:07
閱讀 1621·2019-08-26 10:47
閱讀 3037·2019-08-26 10:41