Data Binding 是 Vue 的一大特色,但我們也可使用 watchAsObservable()
將 data 轉成 Observable 以 RxJS 處理。
Version
macOS Catalina 10.15.4
WebStorm 2020.1
Vue 2.6.11
Vue-rx 6.2
RxJS 6.5.5
watchAsObservable()
當 <input>
的 Number 變化,下方會以 +1
立即改變。
<template>
<div>
<input type="number" v-model="value">
<h1>{{ sum$ }}</h1>
</div>
</template>
<script>
import { pluck, map } from 'rxjs/operators'
let subscriptions = function() {
let sum$ = this.$watchAsObservable('value').pipe(
pluck('newValue'),
map(x => +x + 1)
)
return { sum$ }
}
export default {
name: 'app',
data:() => ({
value: 0
}),
subscriptions,
}
</script>
第 3 行
<input type="number" v-model="value">
<h1>{{ sum$ }}</h1>
<input>
以 v-model
綁定到 value
,下方則顯示 sum$
Observable。
12 行
let sum$ = this.$watchAsObservable('value').pipe(
pluck('newValue'),
map(x => +x + 1)
)
使用 $watchObservable()
將 data 轉成 Observable,由於其包含 newValue
與 oldvalue
兩個 property,因此使用 pluck()
以 newValue
取得目前 value,並使用 map()
改變 Observable 內部值。
value
經過 <input type="number">
的 data binding 後,其 type 成為 String,故使用 +
使其轉型成 Number。
map()
直接改變 Observable 內部值
immediate: true
上例會發現 sum$
沒資料顯示,若要一開始就顯示 1
呢 ?
<template>
<div>
<input type="number" v-model="value">
<h1>{{ sum$ }}</h1>
</div>
</template>
<script>
import { pluck, map } from 'rxjs/operators'
let subscriptions = function() {
let sum$ = this.$watchAsObservable('value', { immediate: true }).pipe(
pluck('newValue'),
map(x => +x + 1)
)
return { sum$ }
}
export default {
name: 'app',
data:() => ({
value: 0
}),
subscriptions,
}
</script>
12 行
let sum$ = this.$watchAsObservable('value', { immediate: true }).pipe(
pluck('newValue'),
map(x => +x + 1)
)
在 $watchAsObservable()
的第二個 argument 傳入 { immediate: true }
,則 sum$
一開始就有 1
可顯示。
Side Effect
若希望 sum$
也能同時有 side effect 呢 ?
<template>
<div>
<input type="number" v-model="value">
<h1>{{ sum$ }}</h1>
</div>
</template>
<script>
import { from } from 'rxjs'
import { pluck, map } from 'rxjs/operators'
let subscriptions = function() {
let sum$ = this.$watchAsObservable('value', { immediate: true }).pipe(
pluck('newValue'),
map(x => +x + 1)
)
from(sum$).subscribe(x => console.log(x))
return { sum$ }
}
export default {
name: 'app',
data:() => ({
value: 0
}),
subscriptions,
}
</script>
18 行
from(sum$).subscribe(x => console.log(x))
原本的 $sum()
要用於顯示,故無法直接使用 subscribe()
,因此使用 from()
另外建立新的 Observable 用於 subscribe()
處理 side effect。
Conclusion
- 除了 DOM event 與 API 可視為 Observable,data 的變化也可視為 Observable
- Observable 的
map()
其實與 Maybe 的map()
意義相同,可視為改變 Observable 內部值 - Computed 與 watch 也有 reactive programming 概念,因此本範例也能使用 computed 與 watch 實現,但別忘了 RxJS 具有豐富的 operator,FRP 是 Vue 所有沒有的
Reference
John Lindquist, Watch Vue.js v-models as Observable with $watchAsObservable and RxJS
Vue-rx, $watchAsObservable()
RxJS, pluck()
RxJS, map()