為什么使用RxJS?
RxJS 未來2~3年是一個很火的一套 Library。
- Reactive Programming 的興起
- Observable 標准化
- 多語言的支持
Reactive Programming 是 RxJS 最重要的核心觀念之一。
認識RxJS
Functional Programming(函數式編程) 是 Rx最重要的觀念之一
Functional Programming 是一種編程規范。
簡單來說,Functional Programming 核心思想就是做運算處理。並用function來思考問題
可以寫成:
我們把每個運算包成一個個不同的function,並用這些function 組合出我們要的結果,這就是最簡單的Functional Programming。
RxJS 可以很好解決異步和事件組合的問題。
RxJS中解決異步事件管理的基本概念如下:
- Observable可觀察對象:表示一個可調用的未來值或者事件的集合。
- Observer觀察者:一個回調函數集合,它知道怎樣去監聽被Observable發送的值
- Subscription訂閱: 表示一個可觀察對象的執行,主要用於取消執行。
- Operators操作符: 純粹的函數,使得以函數編程的方式處理集合比如:
map,filter,contact,flatmap
。 - Subject(主題):等同於一個事件驅動器,是將一個值或者事件廣播到多個觀察者的唯一途徑。
- Schedulers(調度者): 用來控制並發,當計算發生的時候允許我們協調,比如
setTimeout,requestAnimationFrame
。
-
事件流
理解Rx的關鍵是要把任何變化想象成事件流。
-
常見創建類操作符
- from: 可以把
數組
、Promise
、以及Interable
轉化為 Observable - fromEvent:可以把事件轉化為 Observable
- of :接收一系列的數據,並把它們 emit出去
- from: 可以把
-
常見轉換操作符
- map
- mapTo
- pluck
【總結】:
根據不同業務需求(即任何變化-->想象成任何時間維度的事件流),通過不同的運算符,把不同的事件流流合並、轉換成相應的結果(最終的需求業務邏輯)。最重要的一點,他們是自動把數據推送給你,不像傳統那樣需要你去請求,拉取數據。
理解RxJS
操作
Rx
提供了許多接口
小結:
1)常見創建類操作符
from: 可以把數組、Promise、以及Interable轉化為 Observable
fromEvent: 可以把事件轉化為 Observable
of : 接收一系列的數據,並把它們 emit出去
2)常見轉換操作符: map、mapTo、pluck
map的寶珠圖(map是核心主要)
mapTo:(map的延伸擴展)
pluck:(map的延伸擴展)
3)Observable 的性質
三種狀態:next、error、complete
特殊的:永不結束,Never,Empty(結束但不發射),Throw
- 常見工具類操作符:
do
- 常見變換類操作符:
scan
和 常見數學類操作符:reduce
(用的比較頻繁)
- 過濾類操作符:
filter
,take
,first/last
,skip
...
- 兩個常見的創建類操作符:
Interval
,Timer
4)過濾類操作符:Debounce,distinct,distinctUntilChanged
5)合並類操作符:merge、concat、startWith
6)合並類操作符:CombineLatest、withLatestFrom、zip
區別: zip有對齊的特性,withLatestFrom是以源事件流為基准
高階操作符: '拍扁'的作用
RxJS 可視化工具推薦
RxJS 是前端目前為止響應式編程的最佳實踐。很不幸的是我們已經用傳統方式開發很多年了,“evething is stream” 的思想對我們來說不再是順其自然,甚至會有一點蹩腳,尤其是初入 RxJS 的坑。畢竟,有個偉人說過,‘工欲善其事必先利其器’,不是嗎?因此,我們給大家推薦三款可視化的神器,幫助大家對 RxJS 進行感性地了解。
RxViz
這款可視化工具是由 facebook 的 Misha Moroshko 開發。RxViz 可以簡潔的可視化給定的 Observable. 你提供的 RxJS 代碼會被執行,如果最后一個表達式是 Observable, 一個帶着動畫的可視化會出現在眼前。同時,你可以通過修改時間窗口來控制動畫的速率,也可以將可視化 svg 復制下來用於你想用的地方,你同樣可以將可視化分享給其他人。
詳見https://github.com/moroshko/rxviz
RxVision
推薦這款 RxVision 可視化的工具時,我的內心是糾結的。個人來講,我非常喜歡它,但是,尷尬的是作者已經不維護了,擦。但是,它還有一個不得不推薦的理由。請容我慢慢道來。
相信這篇文章是所有前端響應式的殿堂級入門文章,中文也有人翻譯再加工過。文章中的例子,也是經典,詳細闡述了如何用“響應式”的思想構建業務邏輯.
詳見https://github.com/jaredly/rxvision
RxMarbles
這個庫不得不推薦啊,這是響應式大神 staltz 的作品。和前面庫最大的不同是, Observable 的每個 item 是可交互的,你可以拖拽,然后整個 Observable 都會做出相應的改變。
詳見https://github.com/staltz/rxmarbles
ASCII字符來繪制圖表:一切都是流
每一個流都擁有一系列方法,例如map、filter、scan
等
中文教程:http://cn.rx.js.org/manual/tutorial.html
使用 Ngrx
- @ngrx/store
npm install @ngrx/store --save
OR yarn add @ngrx/store
- @ngrx/effects
npm install @ngrx/effects --save
OR yarn add @ngrx/effects
監聽@ngrx/store
發送的動作(即:actions dispatched)
Effects 主要是一個注入服務的類(injectable service classes)
- API 文檔
-
EffectsModule.forRoots( ) 注冊到根模塊下
@NgModule({ imports: [ EffectsModule.forRoot([ FirstEffectsClass, SecondEffectsClass, ]) ] }) export class AppModule { }
-
EffectsModule.forFeature( ) 注冊到任意ng模塊下
@NgModule({ imports: [ EffectsModule.forFeature([ SomeEffectsClass, AnotherEffectsClass, ]) ] }) export class FeatureModule { }
-
Actions
import { Injectable } from '@angular/core'; import { Actions } from '@ngrx/effects'; @Injectable() export class SomeEffectsClass { constructor(private actions$: Actions) {} }
- ofType 篩選動作流類型
import { Injectable } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { tap } from 'rxjs/operators'; @Injectable() export class SomeEffectsClass { constructor(private actions$: Actions) {} @Effect() authActions$ = this.action$.pipe( ofType<LoginAction | LogoutAction>('LOGIN', 'LOGOUT'), tap(action => console.log(action)) ); }
-
- Non-dispatching Effects
{ dispatch: false }
import { Injectable } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { tap } from 'rxjs/operators'; @Injectable() export class SomeEffectsClass { constructor(private actions$: Actions) { } @Effect({ dispatch: false }) logActions$ = this.actions$.pipe( tap(action => console.log(action)) ); }
- Controlling Effects
- OnRunEffects
import { Injectable } from '@angular/core'; import { Actions, Effect, OnRunEffects, EffectNotification, ofType } from '@ngrx/effects'; import { Action } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import { exhaustMap, takeUntil, tap } from 'rxjs/operators'; @Injectable() export class UserEffects implements OnRunEffects { constructor(private actions$: Actions) {} @Effect() updateUser$: Observable<Action> = this.actions$.pipe( ofType('UPDATE_USER'), tap(action => { console.log(action); }) ); ngrxOnRunEffects(resolvedEffects$: Observable<EffectNotification>) { return this.actions$.pipe( ofType('LOGGED_IN'), exhaustMap(() => resolvedEffects$.pipe( takeUntil(this.actions$.pipe(ofType('LOGGED_OUT'))) ) ); } }
- 工具
- mergeEffects 合並所有的Effects
import { mergeEffects } from '@ngrx/effects'; export class MyService { constructor(effects: SomeEffectsClass) { mergeEffects(effects).subscribe(result => { console.log(result); }); } }