@ignorance/vuex-observable
v2.0.1
Published
vuex plugin connected rxjs
Downloads
89
Readme
@ignorance/vuex-observable
Consume Vuex actions as Observables using RxJS 6, inspired by redux-observable.
一览
以下示例使用 vue3
的函数式 API。vue2
的用法与之类似,这里不再赘述。
import { createStore } from "vuex";
import {
combineEpics,
createEpicPlugin,
Epic
} from '@ignorance/vuex-observable'
import {
filter,
map,
} from "rxjs/operators";
const pingEpic: Epic= (action$) => action$.pipe(
filter(action => action.type === "test"),
delay(1000), // Asynchronously wait 1000ms then continue
mapTo({ type: "PONG", payload: 10, isAction: false }) // mapTo('PONG')
);
const vuexObservable = createEpicPlugin();
export default createStore({
state: {
count: 0,
},
mutations: {
PONG(state, num) {
state.count = state.count + num;
},
},
plugins: [vuexObservable],
});
vuexObservable.run(combineEpics(pingEpic));
安装
npm i @ignorance/vuex-observable
使用
// store.js
import { createEpicPlugin } from '@ignorance/vuex-observable'
const vuexObservable = createEpicPlugin();
export default createStore({
// ...
plugins: [vuexObservable],
});
vuexObservable.run();
装配 epic
import { combineEpics, Epic } from '@ignorance/vuex-observable'
// 定义 epic
// 拦截 type 为 “test” 的 action,延时1秒后,转化为一个新的 mutation 发射出去
const pingEpic: Epic= action$ => action$.pipe(
filter(action => action.type === "test"),
delay(1000), // Asynchronously wait 1000ms then continue
mapTo({ type: "PONG", payload: 10, isAction: false }) // mapTo('PONG')
);
// 装配
vuexObservable.run(combineEpics(pingEpic));
食谱
数字跳动
const takeUntilFunc = (endRange: number, currentNumber: number) => {
return endRange > currentNumber
? (val: number) => val <= endRange
: (val: number) => val >= endRange;
};
const positiveOrNegative = (endRange: number, currentNumber: number) => {
return endRange > currentNumber ? 1 : -1;
};
const otometerEpic: Epic = (action$, state$, _, variable = 0) => action$.pipe(
filter(action => action.type === "otometer"),
withLatestFrom(state$),
switchMap(([{ payload: endRange }, state]) => {
return timer(0, 20).pipe(
tap(() => variable = state.currentNumber),
mapTo(positiveOrNegative(endRange, variable)),
startWith(variable),
scan((acc, curr) => acc + curr),
takeWhile(takeUntilFunc(endRange, state.currentNumber)),
)
}),
map(val => ({ type: 'BEAT', payload: val, isAction: false })),
startWith(variable),
)
export default createStore({
state: {
currentNumber: 0,
},
mutations: {
BEAT(state, num) {
state.currentNumber = num
},
},
plugins: [vuexObservable],
});
vuexObservable.run(combineEpics(otometerEpic));
鼠标跟随动画
<template>
<div @mousemove="e => $store.dispatch('follow', e)">
<div
v-for="(pos, index) in $store.state.pos"
:key="index"
:style="{ left: pos.x + 'px', top: pos.y + 'px' }"
></div>
</div>
</template>
const followEpic: Epic= (action$, state$, store) => action$.pipe(
filter(action => action.type === "follow"),
withLatestFrom(state$),
tap(([action, state]) => store.commit("FOLLOW", { index: 0, x: action.payload.clientX, y: action.payload.clientY })),
delay(200),
tap(([action, state]) => store.commit("FOLLOW", { index: 1, x: action.payload.clientX, y: action.payload.clientY })),
delay(200),
tap(([action, state]) => store.commit("FOLLOW", { index: 2, x: action.payload.clientX, y: action.payload.clientY })),
delay(200),
tap(([action, state]) => store.commit("FOLLOW", { index: 3, x: action.payload.clientX, y: action.payload.clientY })),
);
const vuexObservable = createEpicPlugin();
export default createStore({
state: {
pos: [
{ x: 0, y: 0 },
{ x: 0, y: 0 },
{ x: 0, y: 0 },
{ x: 0, y: 0 },
]
},
mutations: {
FOLLOW(state, {index, ...pos}) {
state.pos[index] = pos
}
},
plugins: [vuexObservable],
});
vuexObservable.run(combineEpics(followEpic));