JS中反应式数据存储方案对比 Edata与RxJS, Mobx

简介: Javascript中的状态管理与监测其实一直都在正解决的路上,语言标准方面,从已废弃的[Object.observe API](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/observe),到进入到ES6标准的[Proxy](https://develope

Javascript中的状态管理与监测其实一直都在正解决的路上,语言标准方面,从已废弃的Object.observe API,到进入到ES6标准的Proxy,但都只针对单一对象状态监听,对多层对象实现监听只能借助于外部库,于是产生了我们常见的一些解决方案,如RxJS,提出了新的概念 Reactive Stream,即可被观测的事件流(observable event streams);另一个流行库Mobx则遵循Observer Pattern实现,另外还有一个叫Redux,基于CQRS实现。别说去应用,光是理解这些概念,开发者们都要先掉几根头发。

其实上面几种方案都是围绕着可被观测的对象(Observable Object)这一主题,除了上面几个流行库,在此强烈推荐一个新的更好的解决方案:edata

以下我们用代码来说明对比这几个状态管理库的核心用法:

RxJS observable

export class DataStore {
  public count: BehaviorSubject<number> = new BehaviorSubject(0);
  update(newValue: number) { this.count.next(newValue); }
}

export class ButtonComponent {
  public count: number;
  constructor(private dataStore: DataStore) {
    dataStore.count.subscribe(x => this.count = x);
  }
  onBtnClick() { this.dataStore.update(this.count + 1); }
}

上面的代码,我们在 DataStore 设定了一个可供订阅的 count,初始值为0。ButtonComponentconstructor 添加了一个对 count 的订阅(subscribe),并将变动会存到 this.count 里。按钮点击后触发 dataStore.update() 动作,让数据源中 count 加一,进而触发ButtonComponentthis.count 改变。

Mobx observable

export class DataStore {
  @observable public count: number;
  @action
  update(newValue: number) { this.count = newValue; }
}
export class ButtonComponent {
  public count: number;
  constructor(private dataStore: DataStore) {
    observe(dataStore, 'count', x => this.count = x.newValue);
  }
  onBtnClick() { this.dataStore.update(this.count + 1); }
}

Mobx使用 @observable 装饰器使对象可被观测,以及 @action 装饰器来定义反应式方法,非常直观。不过这里注意一下 observe() 这段代码,有两个坑:1)dataStore 必须为对象,若是 primitive value 必须要包在 observable.box() 中,增加了复杂度;2)count 值必须要变化才会触发,若改变的是同值,则不会触发!

为了演示 Mobx 这个问题,我们设定一个情景,我们加入 isDataLoading 变量表明是否数据是否已加载。

export class DataStore {
  @observable public data:number;
  @action
  update(newValue: number) { this.data = newValue; }
}
export class ButtonComponent {
  public data: number;
  public isDataLoading: boolean;
  constructor(private dataStore: DataStore) {
    observe(dataStore, 'data', x => {
      this.data = x.newValue;
      this.isDataLoading = false;
    });
  }
  onBtnClick() {
    this.isDataLoading = true;
    this.dataStore.update(this.data);
  }
}

这里会产生一个BUG,isDataLoading 一直为 true,因为 this.dataStore.update(this.data) 这句没有改变数值,导致 observe 并没有触发。解决方案就是使用 data = observable.box(),然后使用 data.set(value) 来进行数值设置。

另外还有一个坑,在 Mobx v4 中数组类型判断要非常注意:

var numbers = observable([1,2,3]);
Array.isArray(numbers)  // false ???
Array.isArray(numbers.slice())  // true !!!

简单对比 RxJSMobx,可以看出 RxJS 是比较经典的 pub/sub 模式实现,Mobx 更贴近于声明式 + callback风格,但也有不少坑来自于它本身的实现。

重点来了,

快速上手,不想了解太多概念,坑又少,扩展性好,推荐使用edata

我们来看下同样的例子,使用 edata 如何实现:

import {edataProxy} from 'edata'

export const dataStore = edataProxy({
    data: 0,
    update (newValue: number) {
        this.data = newValue
    }
})

export class ButtonComponent {
  public data: number;
  public isDataLoading: boolean;
  constructor(private dataStore: EdataProxy) {
    dataStore.__watch__('data', x => {
      this.data = x.data.value;
      this.isDataLoading = false;
    })
  }
  onBtnClick() {
    this.isDataLoading = true;
    this.dataStore.update(this.dataStore.data);
  }
}

上面的代码来类比 Mobx,可以看到并没有用到非标准的装饰器语法,更贴近原生,并且没有 Mobx 的那些坑。

这里要注意edataProxy()方法,它使得 dataStore 可以直接使用JS原生方法来存/取对象,但依赖于ES6 Proxy,若要支持IE浏览器,则尽量使用 edata的低阶API

更多edata介绍可以参考另一篇文章:JS中反应式数据存储方案对比 RxJS, Mobx 与 edata

这个库的作者当然也是本文的作者,也需要同学们来一起共建,方式包括但不限于:使用并提出建议,PR,Star,转发等。


badge 同学, 如果觉得本文对你有帮助, 欢迎打赏一杯咖啡~

qr
目录
相关文章
|
JavaScript 定位技术
原生 js 实现类 3d 地图大屏展示自动高亮轮播、显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎
原生 js 实现类 3d 地图大屏展示自动高亮轮播、显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎
251 0
原生 js 实现类 3d 地图大屏展示自动高亮轮播、显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎
|
8月前
|
消息中间件 JavaScript 前端开发
js异步处理方案,js的异步串行与异步并行
js异步处理方案,js的异步串行与异步并行
65 0
|
8月前
|
存储 前端开发
前端学习笔记202306学习笔记第三十七天-js-数据存储2
前端学习笔记202306学习笔记第三十七天-js-数据存储2
44 0
|
9月前
|
前端开发 JavaScript Serverless
前端工程化的前端性能的性能优化方案的渲染层面优化之CSS/JS优化
渲染是一种非常重要的前端性能优化方案,因为它可以在不同的环境中提高网页的响应速度和可接受性。
64 2
|
8月前
|
JavaScript 前端开发
说说你对JavaScript模块化方案的理解和 CommonJS、AMD、CMD、ES6 Module 分别是什么?
模块化是指将一个复杂问题,自顶向下逐层把系统划分为若干模块的过程。对于整个系统来说,这些模块可组合,分解和更换的单元。对于编程领域的模块化就是遵守固定的原则,将一个大文件拆分成多个独立且相互依赖的小模块。简单理解模块化就是在一个js文件中能够引入另一个js文件
|
8月前
|
存储 前端开发
前端学习笔记202305学习笔记第三十二天-js-再次强调数据存储
前端学习笔记202305学习笔记第三十二天-js-再次强调数据存储
31 0
|
8月前
|
存储 前端开发
前端学习笔记202306学习笔记第三十七天-js-数据存储1
前端学习笔记202306学习笔记第三十七天-js-数据存储1
29 0
|
8月前
|
存储 前端开发
前端学习笔记202306学习笔记第三十七天-js-数据存储3
前端学习笔记202306学习笔记第三十七天-js-数据存储3
36 0
|
10月前
|
JavaScript 前端开发 索引
leetcode.328奇偶链表——Javascript实现方案
leetcode.328奇偶链表——Javascript实现方案
66 0
|
JavaScript
js:Vue.js快速原型开发方案@vue/cli模板代码
js:Vue.js快速原型开发方案@vue/cli模板代码
75 0