一、
本文章不會對RxJS的原理進行講解,僅討論如何在vue中對RxJS進行整合
這是一個臺灣開發者編寫的關於RxJS的教程,教程涉及到原理解析、簡易實現,以及常用operator的介紹。
各種常用操作符的作用與例子,可以結合RxJS的官方文件進行查閱。
二、Vue簡單整合RxJS
想要在Vue中使用RxJS,只需要如下引用即可,當然,更多引用方法可以參考官方文件,比如按需引入
import Rx from 'rxjs/Rx'
如果你想在vue中使用RxJS的體驗更好,這裡推薦使用vue-rx這個官方維護的庫,使用如下:
import Vue from 'vue'
import VueRx from 'vue-rx'
import Rx from 'rxjs/Rx'
Vue.use(VueRx, Rx)
複製程式碼
這樣,在Vue例項當中就會多了subscriptions這個鉤子函式,他的用法類似data,使用如下所示:
<template>
<div>
<span>姓名:{{ name$ }}</span>
<span>年齡:{{ age$ }}</span>
<button v-stream:click="setName$">點選設定name的值</button>
</div>
</template>
<script>
export default {
domStreams: [
'setName$'
],
subscriptions () {
return {
age$: Rx.Observable.of(23)
.map(data => data),
name$: this.setName$
.map(e => 'myName')
.startWith('')
}
}
}
</script>
複製程式碼
如上所示,Rx.Observable.of(23)在被訂閱時會被髮出值23,this.setName$則是一個流事件,它在domStreams中定義,實際它是一個Subject(具體可查閱RxJS中對Subject的定義),在使用者點選按鈕的時候則會發出該點選源的資料,如上圖的map operator中,會接收資料來源發出的event物件(這裡我們沒有使用該物件,僅僅是返回一個我們定義的字串'myName'),startWith則是初始化name$的值為空字串,這裡vue-rx已經幫我們做了一個隱式的subscribe繫結,所以值23會馬上發出最後賦值到age$上,進而繫結到檢視,在這裡,我們可以把age$與name$看成是一個有資料來源發出的可觀察流的結果,這條流是響應的,初始發出的值會經過各種operator處理後響應到頁面上。
三、整合vue-rx後使用RxJS
該專案採取了parcel構建、示例包括原生使用與整合vue-rx後使用的對比、事件如何使用、以及常用operator的示例(包含switchMap、concatMap、exhaustMap等的使用場景選擇)
1、建立Observeble
<template>
<div>
<h3>demo2 建立將資料轉化成Observable方式</h3>
<p>字串:{{ str$ }}</p>
<p>
陣列:
<span v-for="(num, index) in arr$" :key="index">{{ num }}</span>
</p>
<p>物件:{{ obj$.a }}</p>
<p>布林值:{{ bool$ }}</p>
<p>promise:{{ promise$ }}</p>
<p>interval: {{ interval$ }}</p>
</div>
</template>
<script>
import Rx from 'rxjs/Rx'
export default {
subscriptions () {
return {
/**
* 普通資料型別都可以用of進行轉換
* promise物件可用from或者fromPromise
* interval可在給定時間區間內發出自增數字
*/
str$: Rx.Observable.of('str'),
arr$: Rx.Observable.of([1, 2, 3]),
obj$: Rx.Observable.of({
a: 'test-obj'
}),
bool$: Rx.Observable.of(true),
promise$: Rx.Observable.fromPromise(this.getPromise()),
interval$: Rx.Observable.interval(1000)
}
},
methods: {
getPromise () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise')
}, 1000)
})
}
}
}
</script>
複製程式碼
建立資料流後,用法類似data,可將資料流的結果跟檢視進行繫結
2、事件的使用
// 沒有整合vue-rx
export default {
...
// 需要獲取dom,所以必須是mounted後執行才能成功
mounted () {
// fromEvent可以將dom繫結事件並轉化成Observable可觀察物件
Rx.Observable.fromEvent(this.$refs['btn'], 'click')
.subscribe(e => {
this.data = '成功獲取data'
})
},
...
}
// 整合vue-rx後
<template>
<button class="btn" v-stream:click="getData$">點選獲取資料</button>
</template>
<script>
export default {
...
// v-stream事件可以統一寫在這裡,具體可以看vue-rx的使用
domStreams: [
'getData$'
],
subscriptions () {
return {
data$: this.getData$
// map操作符主要用於對映資料,這裡我們直接返回了一個字串
.map(e => {
return '成功獲取data'
})
}
}
}
</script>
複製程式碼
3、switchMap、concatMap、exhaustMap使用
一般這幾個operator,會與http請求結合使用,下面我們看些簡單用法,點選後將當前流對映成新的流
<template>
<div>
<h3>demo4 各種map方法運用</h3>
<button class="btn" v-stream:click="getConcatMapCount$">點選獲取concatMapCount$</button>
<p>{{ concatMapCount$ }}</p>
<button class="btn" v-stream:click="getSwitchMapCount$">點選獲取switchMapCount$</button>
<p>{{ switchMapCount$ }}</p>
<button class="btn" v-stream:click="getExhaustMapCount$">點選獲取exhaustMapCount$</button>
<p>{{ exhaustMapCount$ }}</p>
</div>
</template>
<script>
import Rx from 'rxjs/Rx'
export default {
data () {
return {
count: 0
}
},
domStreams: [
'getConcatMapCount$',
'getSwitchMapCount$',
'getExhaustMapCount$'
],
subscriptions () {
/**
* 下面的operator會把一個Observable轉化成另外一個Observable
* 通過返回一個觀察流繼續處理資料
*/
return {
/**
* 當你連續點選按鈕多次獲取資料時,cancatMap會將獲取到的資料按佇列發出
*/
concatMapCount$: this.getConcatMapCount$
.concatMap((e) => {
return Rx.Observable.from(this.getCount())
}),
/**
* 當你連續點選按鈕多次獲取資料時,switchMap只會將最後一個點選發出的值發出,前面發出的值會被吞掉
*/
switchMapCount$: this.getSwitchMapCount$
.switchMap((e) => {
return Rx.Observable.from(this.getCount())
}),
/**
* 當你連續點選按鈕多次時,exhaustMap僅執行一次,在第一次值發出後,才可以繼續點選下一次發出值
*/
exhaustMapCount$: this.getExhaustMapCount$
.exhaustMap(e => {
return Rx.Observable.from(this.getCount())
})
}
},
methods: {
getCount () {
return new Promise((resolve, reject) => {
this.count++
setTimeout(() => {
resolve(this.count)
}, 2000)
})
}
}
}
</script>
複製程式碼
上面的getCount當成是2秒後響應的http請求,當你連續點選的時候,這幾個map operator會有不一樣的行為。
比如concatMap在多次點選後會每隔兩秒就傳送一個遞增的count,而switchMap在多次點選後,會只發出最後一次點選的count,比如我點了3次,switchMapCount$在2秒後會顯示3,而不是1,exhaustMap則是第一次點選沒有響應前不會執行後續的點選操作,直到響應後的點選才有效。
四、關於Rx5與Rx6
上面的倉庫是基於Rx5編寫的示例,而新出的Rx6在api上有些變動,呼叫operator的方式不再是鏈式呼叫,而是通過傳入pipe operator進行組合使用,還有Observable物件的引用也發生了改變,具體可以參考官方文件