
RxJS 是什么
Reactive Programming(響應(yīng)式編程) 是使用異步數(shù)據(jù)流進(jìn)行編程的技術(shù),簡(jiǎn)稱 RP。隨著時(shí)間的推移,產(chǎn)生的一系列事件(鼠標(biāo)點(diǎn)擊、鍵盤輸入等)或數(shù)據(jù)組成的集合,稱為流。RP 就是對(duì)流進(jìn)行編程。RP 本質(zhì)上一種觀察者模式。
RxJS 是 RP 的一種實(shí)現(xiàn)。
Rxjs 有強(qiáng)大的數(shù)據(jù)流組合與控制能力和優(yōu)雅的編碼體驗(yàn),但很難入門。我們來看幾個(gè) Demo 體驗(yàn)下。
1 按鈕只能被點(diǎn)擊一次。
RxJS 的實(shí)現(xiàn)如下
HTML
<button id="sign-in-btn">簽到</button>
JavaScript
var signInBtn = document.querySelector('#sign-in-btn')
Rx.Observable.fromEvent(signInBtn, 'click')
.take(1)// 1次事件后停止事件流
.subscribe(()=> {
signInBtn.textContent = '已簽到'
})
2 模擬三擊事件。
RxJS 的實(shí)現(xiàn)如下
HTML
<button id="triple-click-btn">三擊</button>
JavaScript
var tripleClickBtn = document.querySelector('#triple-click-btn')
Rx.Observable.fromEvent(tripleClickBtn, 'click')
.bufferTime(500) // 收集500ms內(nèi)的點(diǎn)擊事件
.filter(list => list.length === 3)
.subscribe(()=> {
console.log('三擊')
})
3 停止輸入后,如果輸入框內(nèi)容超過2個(gè)字符,觸發(fā)搜索。
RxJS 的實(shí)現(xiàn)如下
HTML
<input type="text" id="input">
JavaScript
Rx.Observable.fromEvent(input, 'keyup')
.debounceTime(500)// 停止輸入 500ms 后觸發(fā)
.map(event => event.target.value) // 輸入框中的值
.filter(value => value.length > 2) // 內(nèi)容超過2個(gè)字符
.switchMap(調(diào)后臺(tái)搜索接口,返回Promise對(duì)象)
.subscribe(data=> {
// 渲染數(shù)據(jù)
})
當(dāng)然,RxJS 也能對(duì)非異步的數(shù)據(jù)進(jìn)行編程。如,找出數(shù)組中偶數(shù)的個(gè)數(shù)
RxJS 的實(shí)現(xiàn)如下:
Rx.Observable.from([1,2,3,4,5])
.filter(num => num % 2 === 0)
.count()
.subscribe(val => {
console.log(`偶數(shù)的個(gè)數(shù)有${val}個(gè)`)
})
概念介紹
可觀察對(duì)象(Observable)
可觀察對(duì)象是多個(gè)值的惰性推送集合。
RxJS 的所有操作都是基于可觀察對(duì)象的。因此,用 RxJs 首先要?jiǎng)?chuàng)建或?qū)?duì)象轉(zhuǎn)化成可觀察對(duì)象。如
Rx.Observable.fromEvent('button', 'click')
上面的代碼將按鈕的點(diǎn)擊事件轉(zhuǎn)化成可觀察對(duì)象。每次按鈕被點(diǎn)擊,可觀察對(duì)象都會(huì)推送該點(diǎn)擊事件。
我們也可以創(chuàng)建一個(gè)可觀察對(duì)象
var observable = Rx.Observable.create(function (observer) {
try {
observer.next(1)
observer.next(2)
} catch(error) {
observer.error(error)
}
setTimeout(() => {
observer.next(3)
observer.complete()
}, 1000)
})
上面的代碼中,調(diào)用 observer.next 來推送值,可以同步或異步的推值。
可觀察對(duì)象可發(fā)送三種類型的通知
- "Next" 通知。寫法:
observer.next(數(shù)據(jù))。 - "Error" 通知, 發(fā)送一個(gè) JavaScript 錯(cuò)誤 或 異常。寫法:
observer.error(錯(cuò)誤對(duì)象) - "Complete" 通知: 不再發(fā)送任何值。寫法:
observer.complete()
可觀察對(duì)象可以發(fā)送任意數(shù)量的 "Next" 類型的通知,但只能發(fā)送一次 "Error" 或 "Complete" 類型的通知。如果發(fā)送了 "Error" 或 "Complete" 通知的話,之后不會(huì)再發(fā)送任何通知了。
觀察者(Observer)
觀察者是可觀察對(duì)象發(fā)送的值的消費(fèi)者。
觀察者只是一組回調(diào)函數(shù)的集合,每個(gè)回調(diào)函數(shù)對(duì)應(yīng)一種可觀察對(duì)象發(fā)送的通知類型。
var observer = {
next: x => console.log('Observer got a next value: ' + x),
error: err => console.error('Observer got an error: ' + err),
complete: () => console.log('Observer got a complete notification'),
}
observable.subscribe(observer)
如果觀察者只在乎可觀察對(duì)象發(fā)送的 Next 通知,可以簡(jiǎn)寫為:
observable.subscribe(x => console.log('Observer got a next value: ' + x))
訂閱(Subscription)
訂閱表示可清理資源的對(duì)象。
當(dāng)觀察者不需要再消費(fèi)可觀察對(duì)象推送的值時(shí),可調(diào)用訂閱對(duì)象上的unsubscribe 函數(shù)來取消訂閱和釋放資源。如
var subscription = observable.subscribe(x => console.log(x));
subscription.unsubscribe() // 取消訂閱
主體(Subject)
主體是一種特殊的可觀察對(duì)象。
主題與普通的可觀察對(duì)象的區(qū)別點(diǎn)在與
- 允許值被多播到多個(gè)觀察者。普通的可觀察者對(duì)象是單播的(僅給一個(gè)的觀察者發(fā)送通知)。
- 可以推送值:擁有next()/error()/complete()方法。
單播的可觀察對(duì)象也可以轉(zhuǎn)化成多播的對(duì)象。如
var source=Rx.Observable.from([1,2,3]);
var subject=new Rx.Subject();
var multicasted=source.multicast(subject);
RxJS 中還有些特殊的主體,如 BehaviorSubject,ReplaySubject,AsyncSubject。感興趣的可以了解下。
操作符
操作符是可觀察對(duì)象上的方法。
可觀察對(duì)象上執(zhí)行操作符,并不會(huì)改變可觀察對(duì)象,而是生產(chǎn)一個(gè)新的可觀察對(duì)象。操作符本質(zhì)上是一個(gè)將某個(gè)可觀察對(duì)象作為輸入然后輸出另一個(gè)可觀察對(duì)象的純函數(shù)。
RxJS 有一大堆操作符,如此強(qiáng)大的原因正是源自于它的操作符。感興趣的可以過一遍RxJS操作符文檔。
用 Marble diagrams可以方便的懂操作符的意思,如下圖

詳情見這里。
調(diào)度器(Scheduler)
調(diào)度器控制著何時(shí)啟動(dòng)可觀察對(duì)象和何時(shí)發(fā)送通知。
詳情見這里。
優(yōu)勢(shì)
容易寫出簡(jiǎn)潔的函數(shù)式編程的代碼。函數(shù)式編程的是一大優(yōu)點(diǎn)是無副作用:不會(huì)修改函數(shù)外部的值,讓你的代碼更健壯,不容易出錯(cuò)。
使用場(chǎng)景
- 處理多個(gè)數(shù)據(jù)序列(有一定順序)。
- 多個(gè)復(fù)雜的異步或者事件組合在一起。
在 Vue.js 中使用RxJS
以模擬三擊事件,舉例,Vue.js 中這么寫:
HTML
<div id="app">
<button @click="clickIt">三擊</button>
</div>
JavaScript
new Vue({
el: '#app',
data: {
clickSubject: null
},
methods: {
clickIt($event) {
this.clickSubject.next()
}
},
mounted() {
this.clickSubject = new Rx.Subject()
this.clickSubject
.bufferTime(500)
.filter(list => list.length === 3)
.subscribe(()=> {
console.log('三擊')
})
}
})
也可以嘗試用 vue-rx。