最近幫潘佳琦解決了一個詭異的問題,而後忽然發現⾃己對觀察者感到迷茫了。typescript
需求是⼀個註銷按鈕,若是是技術機構登錄,就調用技術機構的註銷⽅法,若是是器具用戶登錄,就調⽤器具⽤戶的註銷方法。固然,最優的解決⽅案並不是我下⽂所列的,既然功能不同,那就應該是兩個對象。看來咱們的⾯向對象運用得還不不夠靈活。網絡
註銷代碼以下,只表達思想,別去深究具體的語法:架構
logout(): void { this.departmentService.isLogin$.subscribe((isDepartmentLogin) => { if (isDepartmentLogin) { this.departmentLogout(); } }); this.systemService.isLogin$.subscribe((isTechnicalLogin) => { if (isTechnicalLogin) { this.technicalLogout(); } }); }
看着好像沒啥錯誤啊?訂閱獲取當前登陸用戶狀態,若是departmentService.isLogin$
爲true
,表示是器具用戶登陸,則調用器具用戶的註銷方法;若是是systemService.isLogin$
爲true
,表示是技術機構登陸,則調用技術機構的註銷方法。框架
然而,詭異的事情發生了:微服務
也就是說,這個註銷方法影響了後續的登陸,當時就很懵圈,爲何呢?性能
後來多打了幾條日誌,才發現了問題所在:this
根據Spring
官方的Spring Security and Angular
所述,官方作法是用一個boolean
值來判斷當前是否登陸,從而進行視圖的展現。spa
潘老師在新系統中也是根據官方的推薦進行實現的:設計
let isLogin$ = new BehaviorSubject<boolean>(); login() { ...... this.isLogin$.next(true); } logout() { ...... this.isLogin$.next(false); }
一個可觀察的boolean
對象來判斷當前用戶是否登陸,而後main
組件訂閱這個值,根據是否登陸並結合ngIf
來判斷當前應該顯示登陸組件仍是應用組件。3d
看這個圖你們應該就明白了,問題出在了對subscribe
的理解上。
點擊註銷,發起訂閱,當isLogin$
爲true
的時候就註銷,註銷成功。
下次登陸時,這個訂閱還在呢!而後點擊登陸,執行登陸邏輯,將isLogin$
設置爲true
,觀察者就通知了訂閱者,又執行了一遍註銷邏輯。確定登不上去了。
執行訂閱以後,應該獲取返回值,再執行取消訂閱。
因此,這個問題出在本該訂閱一次,可是subscribe
是隻要訂閱過了,在取消訂閱以前,我一直是這個可觀察對象的觀察者。
想到這我就迷茫了,就是我一訂閱,一直到我取消訂閱以前,這個可觀察對象都要維護着觀察者的列表。
那咱們的網絡請求用的不也是Observable
嗎?只是此Observable
是由HttpClient
構建好返回給咱們的。那咱們訂閱了也沒取消,那它是否是一直維護着這個關係,會不會有性能問題?難道咱們以前的用法都錯了嗎?
這個問題一直困擾了我好多天,知道今天才在Angular
官網上看到相關的介紹,才解決了個人迷茫。
HttpClient.get()
方法正常狀況下只會返回一個可觀察對象,它或者發出數據,或者發出錯誤。有些人說它是「一次性完成」的可觀察對象。
The HttpClient.get() method normally returns an observable that either emits the data or an error. Some folks describe it as a "one and done" observable.
或許英文文檔描述得更準確,one and done
,HttpClient
返回的Observable
對象只執行一次,而後就銷燬。也就是所說的執行一次,或者是next
或者是error
,而後執行complete
銷燬這個對象。
Angular
應該早就想到了維護觀察者帶來的性能問題,才設計一次性的觀察者對象,我卻是杞人憂天了。
RxJS
需求不一樣,有時咱們須要一直訂閱,有時咱們卻想只訂閱一次,不然就會發生一些很詭異的問題。
可是Observable
只提供了一個subscribe
方法,想訂閱一次得手動取消。我想若是Observable
中再添加一個subscribeOnce
的方法,那開發起來會不會比如今更順手?
Google
了一下,真的有啊!StackOverflow
上這老哥和我同樣的想法,有沒有相似subscribeOnce
這樣式的方法?
回答得很棒,舊版本流式調用使用first
方法,新版本使用pipe
,裏面再調用first
方法。
同時這裏說了,若是first
的條件不符合時,會自動取消訂閱。
Angular
中取消訂閱關於如何在Angular
中最優雅地實現取消訂閱,請參考這個問題:Angular/RxJs When should I unsubscribe from Subscription - StackOverflow
八百多個贊,回答得很是好,惋惜我看得是一臉懵逼,可能我開發經驗不夠。您感興趣能夠去看看原回答的實現方式。
若是您有任何的意見或建議,歡迎批評指正。
世上最難的不是學一門技術,而是如何實踐。
沒有教科書,沒有項目參考,全靠一份官方文檔,每一個人都創造着本身的最佳實踐。
一路走來,從華軟開始,也得四個月了,到如今也沒設計出一款滿意的前臺架構。
優秀架構師的設計經驗,確定不會分享給你,而普通的Angular
書籍,不過是一個小的Demo
項目,徹底沒有考慮過項目很龐大的時候應該怎麼組織架構。
最近發現了基於Angular
的前臺微服務框架Mooa
,最近沒時間之後再細看,但願它不要再讓我失望。