淺析AngularJS中的生命周期和延遲處理
這里,我們再討論一些常用的高級的控制反轉(zhuǎn)容器(Inversion of Control containers):延遲加載(lazy-loading),生命周期管理(lifetime management),以及延遲的創(chuàng)建/處理(deferred creation/resolution)。
延遲加載(Lazy-Loading)
所謂延遲加載就是當(dāng)你需要用到對象時候才對其進(jìn)行實(shí)例化。許多依賴注入系統(tǒng)都會在一開始就創(chuàng)建組件,作為它的可依賴項(xiàng)目。不過有時候,直到在應(yīng)用中用到它們之前,你都不會想去實(shí)例化這些組件。Angular 中,一個很好的例子就是,當(dāng)你在配置的時候去設(shè)置一個行為,而該行為又會引用到一些還沒創(chuàng)建的組件。
假設(shè)你想攔截系統(tǒng)內(nèi)建的 $log 服務(wù),因此你把它存在了 $rootScope 里面。當(dāng)然我不建議這樣做,不過這樣舉例比較簡單有效。為了攔截,你在配置的時候用到了 $provide 然后調(diào)用修飾方法。如果這時你想直接引用 $rootScope 的話,由于循環(huán)引用你會拿到個異常。而解決案是通過 $injector 延遲加載 $rootScope 。
下面的代碼只會在 $rootScope 第一次被使用的時候才去加載它。
$provide.decorator(, [, , ($delegate, $injector) { log = $delegate.log.bind($delegate); $delegate.log = (msg) { rs = $injector.get(); (rs.logs === undefined) { rs.logs = []; } rs.logs.push(msg); log(msg); }; $delegate; }]);
之后的調(diào)用都會拿到一樣的單例 $rootScope。 這里有個可用例子。我之前好像聽過有個(不對的)說法(Angular 只支持單例) … 當(dāng)然不是真的。$injector 中的方法就是用來給你管理你的組件的生命周期的。
生命周期管理
生命周期涉及到你如何管理組件的實(shí)例。默認(rèn)情況,當(dāng)你注入一個 Angular 的依賴,依賴注入就會幫你創(chuàng)建它的一個副本然后在你的應(yīng)用里面重用它。大多數(shù)情況下這確實(shí)是我們所期待的。而有些情況下,會要求同一組件的多個實(shí)例。假設(shè)下面的計(jì)數(shù)服務(wù):
Counter($log) { $log.log(); } angular.extend(Counter.prototype, { count: 0, increment: () { .count += 1; .count; } }); Counter.$inject = []; app.service(, Counter);
你的應(yīng)用要跟蹤不同的計(jì)數(shù)器。而你注入該服務(wù)后,總會拿到一樣的計(jì)數(shù)器。這難道是 Angular 的限制?
當(dāng)然不是。重復(fù)一次,通過 $injector 服務(wù)你可以在任何時候創(chuàng)建一個新副本。下面的代碼用了兩個獨(dú)立的計(jì)數(shù)器:
app.run([, , , (rs, c, i) { rs.count = c.count; rs.update = c.increment; rs.update2 = () { c = i.instantiate(Counter); rs.count2 = c.count; rs.update2 = () { c.increment(); rs.count2 = c.count; }; }; }]);
你可以看到計(jì)數(shù)器都是被獨(dú)立的實(shí)例跟蹤的,這里是可用例子。如果你需要經(jīng)常生成新實(shí)例,你可以像這樣注冊服務(wù):
app.factory(, [, (i) { { getCounter: () { i.instantiate(Counter); } }; }]);
產(chǎn)生一個需要的實(shí)例就是那么簡單,而且你可以用你的工廠組件來代替 $injector:
app.run([, , (rs, cf) { c1 = cf.getCounter(), c2 = cf.getCounter(); rs.count = c1.count; rs.update = c1.increment; rs.count2 = c2.count; rs.update2 = () { rs.count2 = c2.increment(); }; }]);
你可以看看這個完整版本的可用例子。如你所見,用 Angular 的內(nèi)建依賴注入是完全有可能管理你組件的生命周期的。那延遲處理(deferred resolution)又怎樣呢 – 比如說,有些組件你需要在 Angular 已經(jīng)配置好之后引入,而且需要用它們的依賴來包裝起來。
延遲處理(Deferred Resolution)
我們已經(jīng)介紹了在 Angular 中可以延遲處理依賴的一種方法。當(dāng)你想包裝某些東西的時候,你可以調(diào)用 $injector 服務(wù)的 instantiate ,然后它可以通過參數(shù)嗅探來解決依賴,看起來就像用 $inject 的靜態(tài)屬性一樣,或者也可以通過檢查你傳給它的數(shù)組來實(shí)現(xiàn)的。也就是說,下面這個是完全有效寫法:
$injector.instantiate(['dependency', Constructor]);
你還可以調(diào)用帶裝飾數(shù)組的方法。假設(shè)你有一個方法依賴于 $log 服務(wù),你可以運(yùn)行時通過延遲處理來調(diào)用它,像下面這樣:
myFunc = [, ($log) { $log.log(); }]; $injector.invoke(myFunc);
你可以看看這個可用例子(打開你的控制臺,看看你按下按鈕之后發(fā)生了什么)。
總結(jié)
綜上所述,Angular 的依賴注入提供了許多高級特性,你在商業(yè)應(yīng)用生產(chǎn)線上會希望并且經(jīng)常會用到它們。factories, services, 和 providers 的便捷讓 Angular 開發(fā)者常常產(chǎn)生錯覺,認(rèn)為這里只有唯一選項(xiàng)可用。而神奇之處在于 $injector 服務(wù),你可以用它生成所需的單例,創(chuàng)建新的組件或者動態(tài)引用帶依賴的方法。
最后要注意的是,你客戶端代碼里面的注入即使在 Angular 之外也是可用的。我們來看一個在 Angular 之外包裝的,通過注入調(diào)用 $log 服務(wù)的例子,點(diǎn)這里。為什么要把 ‘ng' 傳入方法的數(shù)組?它是 Angular 的核心模塊,當(dāng)你包裝你的模塊的時候是會被隱式添加的,但是如果你的指令要生成自己的注入實(shí)例的時候,你就必須顯式添加了。
相關(guān)文章
angular學(xué)習(xí)之ngRoute路由機(jī)制
這篇文章主要介紹了angular學(xué)習(xí)之ngRoute路由機(jī)制,ngRoute是一個Module,提供路由和深層鏈接所需的服務(wù)和指令。有需要的可以了解一下。2017-04-04Angular實(shí)現(xiàn)響應(yīng)式表單
本篇文章主要介紹了Angular實(shí)現(xiàn)響應(yīng)式表單,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08Angular實(shí)現(xiàn)跨域(搜索框的下拉列表)
angular.js 自帶jsonp,實(shí)現(xiàn)跨域,下面來實(shí)現(xiàn)搜索框的下拉列表功能,本文思路明確,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2017-02-02Angular6 用戶自定義標(biāo)簽開發(fā)的實(shí)現(xiàn)方法
這篇文章主要介紹了Angular6 用戶自定義標(biāo)簽開發(fā)的實(shí)現(xiàn)方法,下面我們就通過一個簡單的例子演示Angular6中的這一新功能,小編覺得挺不錯的,現(xiàn)在分享給大家,需要的朋友可以參考下2019-01-01angular4模塊中給標(biāo)簽添加背景圖的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猘ngular4模塊中給標(biāo)簽添加背景圖的實(shí)現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09