随着前端应用的复杂度愈来愈高,如何管理应用的数据已是一个不可回避的问题。当你面对的是 业务场景复杂、需求变更频繁、各类应用数据互相关联依赖的大型前端应用时,你会如何去管理应用的状态数据呢?
咱们认为应用的数据大致上能够分为四类:javascript
RxJS
天生就适合编写异步和基于事件的程序,那么状态数据用什么去管理呢?仍是用RxJS
吗? 合不合适呢?前端
咱们去调研和学习了前端社区已有的优秀的状态管理解决方案,也从一些大牛分享的关于用RxJS
设计数据层的构想和实践中获得了启发:java
RxJS
彻底能够实现诸如Redux
,Mobx
等管理状态数据的功能。observable
来表达,则能够借助RxJS
基于序列且可响应的的特性,以流的方式自由地拼接和组合各类类型的数据,可以更优雅更高效地抽象出可复用可扩展的业务模型。出于以上两点缘由,最终决定基于RxJS
来设计一套管理应用的状态的解决方案。react
对于状态的定义,一般认为状态须要知足如下3个条件:git
event
或者action
对值进行转换,从而获得新的值。那么,RxJS
适合用来管理状态数据吗?答案是确定的!github
首先,由于Observable
自己就是多个值的推送集合,因此第一个条件是知足的!promise
其次,咱们能够实现一个使用dispatch action
模式来推送数据的observable
来知足第二个条件!异步
众所周知,RxJS
中的observable
能够分为两种类型:函数
cold observable
: 推送值的生产者(producer
)来自observable
内部。学习
observable
建立时被定义下来,不可改变。producer
与观察者(observer
) 是一对一的关系,便是单播的。observer
订阅时,producer
都会把预先定义好的若干个值依次推送给observer
。hot observable
: 推送值的producer
来自observable
外部。
producer
与observer
是一对多的关系,便是多播的。observer
订阅时,会将observer
注册到观察者列表中,相似于其余库或语言中的addListener
的工做方式。producer
被触发或执行时,会将值同时推送给全部的observer
;也就是说,全部的observer
共享了hot observable
推送的值。RxJS
提供的BehaviorSubject
就是一种特殊的hot observable
,它向外暴露了推送数据的接口next
函数;而且有“当前值”的概念,它保存了发送给observer
的最新值,当有新的观察者订阅时,会当即从BehaviorSubject
那接收到“当前值”。
那么这说明使用BehaviorSubject
来更新状态并保存状态的当前值是可行的,第三个条件也知足了。
请看如下的代码:
import { BehaviorSubject } from 'rxjs'; // 数据推送的生产者 class StateMachine { constructor(subject, value) { this.subject = subject; this.value = value; } producer(action) { let oldValue = this.value; let newValue; switch (action.type) { case 'plus': newValue = ++oldValue; this.value = newValue; this.subject.next(newValue); break; case 'toDouble': newValue = oldValue * 2; this.value = newValue; this.subject.next(newValue); break; } } } const value = 1; // 状态的初始值 const count$ = new BehaviorSubject(value); const stateMachine = new StateMachine(count$, value); // 派遣action function dispatch(action) { stateMachine.producer(action); } count$.subscribe(val => { console.log(val); }); setTimeout(() => { dispatch({ type: "plus" }); }, 1000); setTimeout(() => { dispatch({ type: "toDouble" }); }, 2000);
执行代码控制台会打印出三个值:
Console 1 2 4
上面的代码简单实现了一个简单管理状态的例子:
plus
以后的状态值: 2toDouble
以后的状态值: 4实现方法挺简单的,就是使用BehaviorSubject
来表达状态的当前值:
dispatch
函数使producer
函数执行producer
函数在内部调用了BehaviorSubject
的next
函数,推送了新数据,BehaviorSubject
的当前值更新了,也就是状态更新了。不过写起来略微繁琐,咱们对其进行了封装,优化后写法见下文。
咱们自定义了一个操做符state
用来建立一个可以经过dispatch action
模式推送新数据的BehaviorSubject
,咱们称她为stateObservable
。
const count$ = state({ // 状态的惟一标识名称 name: "count", // 状态的默认值 defaultValue: 1, // 数据推送的生产者函数 producer(next, value, action) { switch (action.type) { case "plus": next(value + 1); break; case "toDouble": next(value * 2); break; } } });
在你想要的任意位置使用函数dispatch
派遣action
便可更新状态!
dispatch("count", { type: "plus" })
RxJS
的一大优点就在于可以统一同步和异步,使用observable
处理数据你不须要关注同步仍是异步。
下面的例子咱们使用操做符from
将promise
转换为observable
。
observable
做为状态的初始值(首次推送数据)const todos$ = state({ name: "todos", // `observable`推送的数据将做为状态的初始值 initial: from(getAsyncData()) //... });
producer
推送observable
const todos$ = state({ name: "todos", defaultValue: [] // 数据推送的生产者函数 producer(next, value, action) { switch (action.type) { case "getAsyncData": next( from(getAsyncData()) ); break; } } });
执行getAsyncData
以后,from(getAsyncData())
的推送数据将成为状态的最新值。
因为状态todos$
是一个observable
,因此能够很天然地使用RxJS
操做符转换获得另外一个新的observable
。而且这个observable
的推送来自todos$
;也就是说只要todos$
推送新数据,它也会推送;效果相似于Vue
的计算属性。
// 未完成任务数量 const undoneCount$ = todos$.pipe( map(todos => { let _conut = 0; todos.forEach(item => { if (!item.check) ++_conut; }); return _conut; }) );
咱们可能会在组件的生命周期内订阅observable
获得数据渲染视图。
class Todos extends React.Component { componentWillMount() { todos$.subscribe(data => { this.setState({ todos: data }); }); } }
咱们能够再优化下,利用高阶组件封装一个装饰器函数@subscription
,顾名思义,就是为React组件订阅observable
以响应推送数据的变化;它会将observable
推送的数据转换为React组件的props
。
@subscription({ todos: todos$ }) class TodoList extends React.Component { render() { return ( <div className="todolist"> <h1 className="header">任务列表</h1> {this.props.todos.map((item, n) => { return <TodoItem item={item} key={item.desc} />; })} </div> ); } }
使用RxJS
越久,越使人受益不浅。
observable
序列提供了较高层次的抽象,而且是观察者模式,能够尽量地减小各组件各模块之间的耦合度,大大减轻了定位BUG和重构的负担。observable
序列来编写代码的,因此遇到复杂的业务场景,总能按照必定的顺序使用observable
描述出来,代码的可读性很强。而且当需求变更时,我可能只须要调整下observable
的顺序,或者加个操做符就好了。不再必由于一个复杂的业务流程改动了,须要去改好几个地方的代码(并且还容易改出BUG,笑~)。因此,以上基于RxJS
的状态管理方案,对咱们来讲是一个必需品,由于咱们项目中大量使用了RxJS
,若是状态数据也是observable
,对咱们抽象可复用可扩展的业务模型是一个很是大的助力。固然了,若是你的项目中没有使用RxJS
,也许Redux
和Mobx
是更合适的选择。
这套基于RxJS
的状态管理方案,咱们已经用于开发公司的商用项目,反馈还不错。因此咱们决定把这套方案整理成一个js lib
,取名为:Floway
,并在github
上开源:
欢迎你们star
,更欢迎你们来共同交流和分享RxJS
的使用心得!
参考文章: