04月03, 2017

React + Rx 模仿 Angular 模式

一、前言

其实对于软件开发模式来说,Angular 有着整套的一条龙服务,而 React 只是单纯的解决 View 层的问题,如果只是使用 React 开发项目会或多或少有点麻烦。下面主要讨论下数据层的东西,之前出了一个 mobx 来解决这个问题,但是这里想换个思路套用一下。其实 mobx 主要是声明了 @action, @computed, @observable 等元素来做到将 Store 作为一个可监控的源头,自动做到 VM 的效果,下面我也是,不过我采用 Rx 来做这个事情,简单利用下这种思想,能不能不污染我的原始数据(因为如果是双向的一种数据结构,那么就会被包装成监听类型的数据结构),这样我们调试的时候依然看到的是清晰的数据,并且还可以解决最初 Flux 库的一些麻烦,经过 Rx 改造的结果,可能比 mobx 的可测试性更好一些,具体就不说了,先谈 DEMO。

如果不想看文章的可以直接看代码 GitHub

二、整体的一个思想

service

这里有几个元素:

  1. 服务层 (Service)
  2. 数据 (Data)
  3. 响应式作业流程 (Rx-Subject)
  4. 视图 (View)
  5. 视图内部的状态更新 (setState)

他们之间的依赖关系:

  1. Service 依赖:Data、Rx-Subject
  2. View 层依赖 Data

这样基本做到了视图和视图数据的干净依赖,通过 Service 的封装进行了解耦。在视图内部又是自更新的,所以对内的更新也是对外封闭的,只对注入的 Service 依赖的原始数据有关。

三、项目的目录

├── README.md
├── dist // 生成目录
│   └── app.js
├── index.html // 入口页面
├── package.json
├── src // 开发源码
│   ├── App.tsx // 应用入口
│   ├── components // 组件类
│   ├── containers // 容器类
│   ├── filters // 过滤器
│   ├── interface // 接口
│   ├── services // 服务类(单例)
│   ├── tsconfig.json // ts 的配置文件
│   └── utils // 工具类
├── webpack.config.js
└── yarn.lock

主要拆分的目录如上,这样如果一个系统的服务有很多可重用的情况下,就比较好抽离出来了,针对数据,如果后端是原始数据也需要一个数据管道的过滤层。

为什么要使用 TypeScript 呢? 因为 Rx 是数据流的一个操作过程,如果对过程中的数据类型是鸭子类型的话,就比较不好跟踪和判断了,所以我这里采用了 TypeScript,这样对数据也更灵敏一些。

配置其实没多少行,这里就不多介绍了,然后主要介绍下 containersservices。这里我们的场景很简单,就是有 2 个按钮:添加删除,数据就是不同的字符串,直接用 DOM 展示了出来。

jj

四、容器类 (containers)

containers/TokenList

// 1. 挂载后从服务层获取数据,并且订阅这个数据的更新
componentDidMount() {
    // this.subject$ 是 Rx-Subject 的实例
    this.subject$ = TokenListService.get();

    this.subject$.subscribe((tokens: IToken[]) => {
        this.setState({ tokens });
    });
}

// 2. 对数据的操作都通过服务层
// 数据添加
TokenListService.post("lulin");
// 数据删除
TokenListService.del(id);

其实对于容器类来说,在订阅的时候已经定义好了界面的同步刷新,那么这个同步刷新数据的流入肯定就是在服务层去声明的,在后面服务层的代码中可以看出。

这里其实可以类比 Flux 来看,Flux 的流程是:

  1. Action
  2. Dispatch
  3. Store mixin EventEmitter 来衍生成事件响应的数据层
  4. 最后通过 switch 来调用 emit 或者数据操作的状态
  5. 最后返回 state,每一个操作可能会都声明一次 this.setState,并且还需要绑定多个事件的方式

对比 Rx 的方式:

  1. 定义 Subject 实例
  2. 然后在 Service 实例做数据操作,然后在流中流入数据到订阅
  3. 如果是多个流的声明,在挂载的时候就需要定义多个流,但是流是可以合并,取消等操作的

总体上来看,代码量估计是有减少的,流程也更短。

五、服务层 (services)

services/TokenList.service

// 生成私有的 tokens 视图数据
let tokens: Array<IToken> = [];
// 生成个流程作业的实例
let tokens$ = new Subject<IToken[]>();

// 基本上你要流入新数据只需要调用这个
tokens$.next(tokens);
// 这是保证触发,但是也可以不加
tokens$.complete();

整个服务层来讲,由于 Rx 的对外封装,提供简单接口的方式,这里也少了一些模板和多余的数据。

=..= 国外很多都玩过了吧,这里弄一个简单的例子来描述一下这种方式,Rx 是可以兼容到 IE8 的,mobx 不行,所以如果有需要是可以完美组合的,并且有测试需求的估计也比 mobx 好一些,大多数抽象得好,不需要测试视图。

本文链接:http://www.60sky.com/post/react-rx-1.html

-- EOF --

Comments