介紹RxJS在Angular中的應(yīng)用

RxJS是一種針對異步數(shù)據(jù)流編程工具,或者叫響應(yīng)式擴展編程;可不管如何解釋RxJS其目標就是異步編程,Angular引入RxJS為了就是讓異步可控强霎、更簡單。

而今就是要探討什么是Observable蓉冈、observer城舞、operator、Submit寞酿、EventEmmit家夺,以及如何去使用它們。

什么是Observable伐弹?

Observable只是一個普通函數(shù)拉馋,要想讓他有所作為,就需要跟observer一起使用惨好;前者是受后者是攻煌茴。而這個observer(后面我們會介紹)只是一個帶有 nexterror日川、complete 的簡單對象而已蔓腐。最后,還需要通過 subscribe 訂閱來啟動Observable逗鸣;否則它是不會有任何反應(yīng);可以理解為陌為了他們能在一起而提供的環(huán)境*绰精,而訂閱也會返回一個可用于取消操作(在RxJS里叫 unsubscribe)撒璧。

當Observable設(shè)置觀察者后,而連接并獲取原始數(shù)據(jù)的這個過程叫生產(chǎn)者笨使,可能是DOM中的 click 事件卿樱、input 事件、或者更加復(fù)雜的HTTP通信硫椰。

為了更好理解繁调,先從一個簡單的示例開始:

import { Component } from '@angular/core';
import { Observable, Subscription } from 'rxjs';

@Component({
  selector: 'app-home',
  template: `<input type="text"> `
})
export class HomeComponent {
  ngOnInit() {
    const node = document.querySelector('input[type=text]');

    // 第二個參數(shù) input 是事件名萨蚕,對于input元素有一個 oninput 事件用于接受用戶輸入
    const input$ = Observable.fromEvent(node, 'input');
    input$.subscribe({
      next: (event: any) => console.log(`You just typed ${event.target.value}!`),
      error: (err) => console.log(`Oops... ${err}`),
      complete: () => console.log(`Complete!`)
    });
  }
}

示例中 Observable.fromEvent() 會返回一個Observable,并且監(jiān)聽 input 事件蹄胰,當事件被觸發(fā)后會發(fā)送一個 Event 給對應(yīng)的observer觀察者岳遥。

什么是observer?

observer非常簡單裕寨,像上面示例中 subscribe 訂閱就是接收一個 observer 方法浩蓉。

一般在Angular我們 subscribe 會這么寫:

input$.subscribe((event: any) => {

});

從語法角度來講和 subscribe({ next, error, complete }) 是一樣的。

當Observable產(chǎn)生一個新值時宾袜,會通知 observer 的 next()捻艳,而當捕獲失敗可以調(diào)用 error()

當Observable被訂閱后庆猫,除非調(diào)用observer的 complete()unsubscribe() 取消訂閱兩情況以外认轨;會一直將值傳遞給 observer。

Observable的生產(chǎn)的值允許經(jīng)過一序列格式化或操作月培,最終得到一個有價值的數(shù)據(jù)給觀察者嘁字,而這一切是由一序列鏈式operator來完成的,每一個operator都會產(chǎn)生一個新的Observable节视。而我們也稱這一序列過程為:流拳锚。

什么是operator?

正如前面說到的寻行,Observable可以鏈式寫法霍掺,這意味著我們可以這樣:

Observable.fromEvent(node, 'input')
  .map((event: any) => event.target.value)
  .filter(value => value.length >= 2)
  .subscribe(value => { console.log(value); });

下面是整個順序步驟:

  • 假設(shè)用戶輸入:a
  • Observable對觸發(fā) oninput 事件作出反應(yīng),將值以參數(shù)的形式傳遞給observer的 next()拌蜘。
  • map() 根據(jù) event.target.value 的內(nèi)容返回一個新的 Observable杆烁,并調(diào)用 next() 傳遞給下一個observer。
  • filter() 如果值長度 >=2 的話简卧,則返回一個新的 Observable兔魂,并調(diào)用 next() 傳遞給下一個observer。
  • 最后举娩,將結(jié)果傳遞給 subscribe 訂閱塊析校。

你只要記住每一次 operator 都會返回一個新的 Observable,不管 operator 有多少個铜涉,最終只有最后一個 Observable 會被訂閱智玻。

不要忘記取消訂閱

為什么需要取消訂閱

Observable 當有數(shù)據(jù)產(chǎn)生時才會推送給訂閱者,所以它可能會無限次向訂閱者推送數(shù)據(jù)芙代。正因為如此吊奢,在Angular里面創(chuàng)建組件的時候務(wù)必要取消訂閱操作,以避免內(nèi)存泄漏纹烹,要知道在SPA世界里懂得擦屁股是一件必須的事页滚。

unsubscribe

前面示例講過召边,調(diào)用 subscribe() 后,會返回一個 Subscription 可用于取消操作 unsubscribe()裹驰。最合理的方式在 ngOnDestroy 調(diào)用它隧熙。

ngOnDestroy() {
    this.inputSubscription.unsubscribe();
}

takeWhile

如果組件有很多訂閱者的話,則需要將這些訂閱者存儲在數(shù)組中邦马,并組件被銷毀時再逐個取消訂閱贱鼻。但,我們有更好的辦法:

使用 takeWhile()
operator滋将,它會在你傳遞一個布爾值是調(diào)用 next() 還是 complete()邻悬。

private alive: boolean = true;
ngOnInit() {
  const node = document.querySelector('input[type=text]');

  this.s = Observable.fromEvent(node, 'input')
    .takeWhile(() => this.alive)
    .map((event: any) => event.target.value)
    .filter(value => value.length >= 2)
    .subscribe(value => { console.log(value) });
}

ngOnDestroy() {
  this.alive = false;
}

簡單有效,而且優(yōu)雅随闽。

Subject

如果說 Observableobserver 是攻受結(jié)合體的話父丰,那么 Subject 就是一個人即攻亦受。正因為如此掘宪,我們在寫一個Service用于數(shù)據(jù)傳遞時蛾扇,總是使用 new Subject

@Injectable()
export class MessageService {
    private subject = new Subject<any>();

    send(message: any) {
        this.subject.next(message);
    }

    get(): Observable<any> {
        return this.subject.asObservable();
    }
}

當F組件需要向M組件傳遞數(shù)據(jù)時魏滚,我們可以在F組件中使用 send()镀首。

constructor(public srv: MessageService) { }

ngOnInit() {
    this.srv.send('w s k f m?')
}

而M組件只需要訂閱內(nèi)容就行:

constructor(private srv: MessageService) {}

message: any;
ngOnInit() {
    this.srv.get().subscribe((result) => {
        this.message = result;
    })
}

EventEmitter

其實EventEmitter跟RxJS沒有直接關(guān)系,因為他是Angular的產(chǎn)物鼠次,而非RxJS的東西更哄。或者我們壓根沒必要去談腥寇,因為EventEmitter就是Subject成翩。

EventEmitter的作用是使指令或組件能自定義事件

@Output() changed = new EventEmitter<string>();

click() {
    this.changed.emit('hi~');
}
@Component({
  template: `<comp (changed)="subscribe($event)"></comp>`
})
export class HomeComponent {
  subscribe(message: string) {
     // 接收:hi~
  }
}

上面示例其實和上一個示例中 MessageService 如出一轍赦役,只不過是將 next() 換成 emit() 僅此而已麻敌。

結(jié)論

RxJS最難我想就是各種operator的應(yīng)用了,這需要一些經(jīng)驗的積累掂摔。

RxJS很火很大原因我認還是提供了豐富的API术羔,以下是摘抄:

創(chuàng)建數(shù)據(jù)流:

  • 單值:of, empty, never
  • 多值:from
  • 定時:interval, timer
  • 從事件創(chuàng)建:fromEvent
  • 從Promise創(chuàng)建:fromPromise
  • 自定義創(chuàng)建:create

轉(zhuǎn)換操作:

  • 改變數(shù)據(jù)形態(tài):map, mapTo, pluck
  • 過濾一些值:filter, skip, first, last, take
  • 時間軸上的操作:delay, timeout, throttle, debounce, audit, bufferTime
  • 累加:reduce, scan
  • 異常處理:throw, catch, retry, finally
  • 條件執(zhí)行:takeUntil, delayWhen, retryWhen, subscribeOn, ObserveOn
  • 轉(zhuǎn)接:switch

組合數(shù)據(jù)流:

  • concat,保持原來的序列順序連接兩個數(shù)據(jù)流
  • merge乙漓,合并序列
  • race级历,預(yù)設(shè)條件為其中一個數(shù)據(jù)流完成
  • forkJoin,預(yù)設(shè)條件為所有數(shù)據(jù)流都完成
  • zip簇秒,取各來源數(shù)據(jù)流最后一個值合并為對象
  • combineLatest鱼喉,取各來源數(shù)據(jù)流最后一個值合并為數(shù)組

另秀鞭,最好使用 $ 結(jié)尾的命名方式來表示Observable趋观,例:input$扛禽。

happy coding!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市皱坛,隨后出現(xiàn)的幾起案子编曼,更是在濱河造成了極大的恐慌,老刑警劉巖剩辟,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掐场,死亡現(xiàn)場離奇詭異,居然都是意外死亡贩猎,警方通過查閱死者的電腦和手機熊户,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吭服,“玉大人嚷堡,你說我怎么就攤上這事⊥ё兀” “怎么了蝌戒?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沼琉。 經(jīng)常有香客問我北苟,道長,這世上最難降的妖魔是什么打瘪? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任友鼻,我火速辦了婚禮,結(jié)果婚禮上瑟慈,老公的妹妹穿的比我還像新娘桃移。我一直安慰自己,他們只是感情好葛碧,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布借杰。 她就那樣靜靜地躺著,像睡著了一般进泼。 火紅的嫁衣襯著肌膚如雪蔗衡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天乳绕,我揣著相機與錄音绞惦,去河邊找鬼。 笑死洋措,一個胖子當著我的面吹牛济蝉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼王滤,長吁一口氣:“原來是場噩夢啊……” “哼贺嫂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起雁乡,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤第喳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后踱稍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體曲饱,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年珠月,在試婚紗的時候發(fā)現(xiàn)自己被綠了扩淀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡啤挎,死狀恐怖引矩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侵浸,我是刑警寧澤旺韭,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站掏觉,受9級特大地震影響区端,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜澳腹,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一织盼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酱塔,春花似錦沥邻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蕊玷,卻和暖如春邮利,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垃帅。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工延届, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贸诚。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓方庭,卻偏偏與公主長得像厕吉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子械念,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容