RxJS實踐,Vue如何集成RxJS

NO IMAGE

一、

本文章不會對RxJS的原理進行講解,僅討論如何在vue中對RxJS進行集成

1、30天精通 RxJS

這是一個臺灣開發者編寫的關於RxJS的教程,教程涉及到原理解析、簡易實現,以及常用operator的介紹。

2、學習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

github地址

該項目採取了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對象的引用也發生了改變,具體可以參考官方文檔

相關文章

webpack4搭建現代Hybirdh5工程

如何使用imageset適配移動端高清屏圖片

如何在webpack中做預渲染降低首屏空白時間

HybirdApp模式下,js如何與客戶端交互