RxJS结合vue-rx, Akita的介绍和使用

作者:神秘网友 发布时间:2020-10-24 00:44:28

RxJS结合vue-rx, Akita的介绍和使用

RxJS结合vue-rx, Akita的介绍和使用

介绍: 解决异步事件。它将一切数据包括HTTP请求,DOM事件或者普通数据等包装成流的形式,然后利用强大丰富的操作符(Operators )对流进行处理,使得用户可以用同步编程的方式处理异步数据。

基本概念:Observable 类似于函数声明,subscribe类似于函数调用,调用时传入回调函数Observable 对象来执行,执行后的返回值称为Subscription ,可以停止后续Observable 的执行

Observable (可观察对象): 。是一可观察序列,简单来说数据就在Observable中流动,用户可以使用各种operator对流进行处理。

//create方法创建一个Observable
var observable = Observable
    .create(function(observer) { 
        observer.next('Jerry'); 
        observer.next('Anna');
    })

// subscribe订阅 observable 
console.log('start');   
observable.subscribe(function(value) {
    console.log(value);
})
console.log('end');

// 输出
start
Jerry
Anna
end

Observer (观察者): 一个回调函数的集合,它知道如何去监听由 Observable 提供的值,其包括以下三个方法
Next – 表示数据正常流动,没有出现异常
Error – 表示流中出错,可能是运行出错,http报错等
Complete – 表示流结束,不再发射新的数据

1、 要使用观察者,需要把它提供给Observable的subscribe方法。
2、 在一个流的生命周期中,error和complete只会触发其中一个,可以有多个next(表示多次发射数据),直到complete或者error。

  console.log('just before subscribe');
  const observe = {
      next: x => console.log('got value ' + x),
      error: err => console.error('something wrong occurred: ' + err),
      complete: () => console.log('done'),
  };

  // 订阅这个 observable
  // 只有在订阅之后,才会在流Observable变化的时候,调用observer提供的方法,并通知它
 observable.subscribe(observe);
 console.log('just after subscribe');
 
  // 输出
  just before subscribe
  got value 1
  got value 2
  got value 3
  just after subscribe
  got value 4
  done

Subscription (订阅): 表示 Observable 的执行,Subscription基本上只有一个unsubscribe()函数,这个函数用来释放资源或取消Observable执行,清理由Subscription占用的资源。

let subscription = observable.subscribe(observe);
  setTimeout(() => {
      subscription.unsubscribe();
  }, 1000);

Operators (操作符):
操作符是Observable类型上的方法,用来处理集合。当操作符被调用时,它们不会改变已经存在的Observable实例。相反,它们会返回一个新的Observable,它的subscription逻辑基于第一个Observable。

RxJS结合vue-rx, Akita的介绍和使用
其实部分操作符从功能来讲可能会觉得区别不太大,区分起来也比较困难,我们可以查阅它们的弹珠图来方便我们区分。

安装并配置vue-rx的依赖包

yarn add rxjs
yarn add vue-rx
//main.js文件
import VueRx from "vue-rx";
Vue.use(VueRx);

vue-rx会新增一个钩子函数subscriptions,和vue的data类似的使用;以及用来存放事件的一个domStreams数组

事件的使用:

<template>
	<ul>
	 <li v-for="(num, index) in numList" :key="index" v-stream:click="{
	  subject: getNum$,
	  data: {
	   'index': index,
	   'num': num
	  }
	 }">{{ num }}</li>
	</ul>
	<p>点击的数字为:{{ num$.index }}</p>
	<p>点击的数字下标为:{{ num$.num }}</p>
</template>
<script>
	import { Observable } from 'rxjs'
	export default {
	  name: 'HelloWorld',
	  data () {
		   return {
		   	numList: [1, 2, 3]
		  }
	  },
	  domStreams: ['getNum$',],
	  subscriptions() {
	    return {
		   num$: this.getNum$
		    // 从传入的对象中获取key为data的value,传入下一个operator
		    .pluck('data')
		    .map(data => data)
		    // 因为视图引用了num$.index,所以这里要初始化num$为对象,避免报错
		    .startWith({}) 
	  }
	}
</script>

关于switchMap、concatMap、exhaustMap的使用:

domStreams: ['getConcatMapCount$', 'getSwitchMapCount$', 'getExhaustMapCount$'],
subscriptions() {
    return {
      // 当你连续点击按钮多次获取数据时,cancatMap会将获取到的数据按队列发出
      concatMapCount$: this.getConcatMapCount$.concatMap(e => {
        return new Observable.from(this.getCount());
      }),
      // 当你连续点击按钮多次获取数据时,switchMap只会将最后一个点击发出的值发出,前面发出的值会被吞掉
      switchMapCount$: this.getSwitchMapCount$.switchMap(e => {
        return new Observable.from(this.getCount());
      }),
      // 当你连续点击按钮多次时,exhaustMap仅执行一次,在第一次值发出后,才可以继续点击下一次发出值
      exhaustMapCount$: this.getExhaustMapCount$.exhaustMap(e => {
        return new Observable.from(this.getCount())
      })
    }
  },
  methods: {
    getCount() {
      return new Promise((resolve, reject) => {
        this.count ++;
        setTimeout(() => {
          resolve(this.count);
        }, 1000);
      })
    }
  }

关于forkJoin的使用:

会在每个被合并的流都发出结束信号时发射一次数据,也就是只触发一次回调
 methods: {
 	getEmergencyMapData() {
      const $viewData = queryEmergencyViewMapData(this.$route.query.id).pipe(
        map(data => [data]),
        catchError(() => {
          return of([]);
        })
      );

      const $viewData2 = queryEmergencyIconpointMapData(
        this.$route.query.id
      ).pipe(
        map(data => [data]),
        catchError(() => {
          return of([]);
        })
      );

      const $viewData3 = queryEmergencyDecisionMapData(
        this.$route.query.id
      ).pipe(
        map(data => [data]),
        catchError(() => {
          return of([]);
        })
      );
      
	  //同时发送三个请求,将返回值组成一个新的数组
      forkJoin($viewData, $viewData2, $viewData3) 
        .pipe(
          map(([view1, view2, view3]) => [...view1, ...view2, ...view3])
          tap(data => {
             console.log(data )
          })
        )
        .subscribe(); //在methods中必须订阅了才会发送请求
    }
 }

Akita介绍: Akita是一种基于RxJS的状态管理模式,它采用Flux中的多个数据存储和Redux中的不可变更新的思想,以及流数据的概念,来创建可观察的数据存储模型。

安装并配置Akita的依赖包

yarn add @datorama/akita
//main.js文件
import "./plugins/akita"; 
//akita.js文件
import { persistState } from "@datorama/akita";
import { akitaConfig } from "@datorama/akita";

if (process.env.NODE_ENV === "development") {
  import("@datorama/akita").then(modules => {
    modules.akitaDevtools();
  });
}

akitaConfig({
  resettable: true
});

persistState({
  include: ["session"],
  storage: {
    getItem: key => sessionStorage.getItem(key),
    setItem: (key, value) => sessionStorage.setItem(key, JSON.stringify(value)),
    removeItem: key => sessionStorage.removeItem(key)
  }
});

Akita的使用:
在src下新建store文件,目录结构如下
RxJS结合vue-rx, Akita的介绍和使用

xxx.store.js: 存放所要管理的状态

import { createStore } from "@datorama/akita";

const initialState = {
  //存放ui展示的变量,一般为布尔值
  UI: {
    isShowPlanPanel: false, //预案详情
  },
  //存放其他变量,字符串,数组,对象等
  selectPlanId: null, //预案id
};

export const basisDataStore = createStore(initialState, {
  name: "basisData"
});

xxx.service.js: 操作store数据的方法的集合,比如对该数据的修改、增加、删除等等

import { basisDataStore } from "./basis-data.store";

export function isShowPlanPanel(show) {
  basisDataStore.update(state => ({
    ...state,
    UI: { ...state.UI, isShowPlanPanel: show }
  }));
}
export function setSelectPlanId(id) {
  basisDataStore.update(state => ({
    ...state,
    selectPlanId: id
  }));
}

xxx.query.js: 将store数据暴露给外部,方便组件的调用

import { createQuery } from "@datorama/akita";
import { basisDataStore } from "./basis-data.store";

export const basisDataQuery = createQuery(basisDataStore);

export const $isShowPlanPanel = basisDataQuery.select(
  state => state.UI.isShowPlanPanel
);
export const $selectPlanId = basisDataQuery.select(state => state.selectPlanId);

参数传递的顺序依次是:

1.、组件A调用service.js的方法,为selectPlanId和isShowPlanPanel赋值

	import {
	  setSelectPlanId,
	  isShowPlanPanel
	} from "../../store/basis-data/basis-data.service";

    onClickRow(row) {
      isShowPlanPanel(true);
      setSelectPlanId(row.id);
    }

2、组件B在subscriptions钩子函数中监听selectPlanId和isShowPlanPanel的变化

import { $selectPlanId } from "../../store/basis-data/basis-data.query";
subscriptions() {
    return {
      isShowPlanPanel: $isShowPlanPanel,
      planData: $selectPlanId.pipe(
        mergeMap(planId => queryPlanDetails(planId )),//发送请求,获取详情
        map(data => {
          if (data) {
            //数据处理
            ...
            return data
          }
        })
      )
    };
  },

RxJS结合vue-rx, Akita的介绍和使用相关教程

  1. grain-Edu-Note part22 spring结合redis并使用友好的序列化方式

    grain-Edu-Note part22 spring结合redis并使用友好的序列化方式 !-- spring boot redis 缓存引入-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- lecttuce 缓存连接池-- depend

  2. C++核心准则T.5:结合使用泛型和面向对象技术应该增强它们的效果

    C++核心准则T.5:结合使用泛型和面向对象技术应该增强它们的效果而不是成本 T.5: Combine generic and OO techniques to amplify their strengths, not their costs T.5:结合使用泛型和面向对象技术应该增强它们的效果而不是成本 Reason(原因) Generic and

  3. 源码分析-Condition结合AQS解析

    源码分析-Condition结合AQS解析 Condition 如图,java.util.concurrent.locks包下,与AQS同级 主要方法就是 await() :使当前持有锁的线程进入等待 signal() : 唤醒等待的线程重新排队去抢锁 Condition常用于生产者消费者场景, 负责生产者消费者的阻塞用途 不

  4. 多线程结合线程池方式对文件批量下载到本地和压缩文件到浏览器

    多线程结合线程池方式对文件批量下载到本地和压缩文件到浏览器 1.批量下载到本地: 首先先了解Executorsc创建的4中线程池的使用: 1)newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 2

  5. 结合namespace自己搭建MVC框架

    结合namespace自己搭建MVC框架 结合namespace自己搭建MVC框架 MVC基本介绍 MVC是这3个单词的缩写: Model:模型,专门用来处理数据的 View:视图,专门用来显示内容(保存一些html文件) Controller:控制器(中控器),用来分发任务 (命令模型处理数据、命

  6. docker,jenkins与ansible结合实践

    docker,jenkins与ansible结合实践 1. 安装依赖 [[emailprotected] ~]# rpm -ivh /home/allen/container-selinux-2.107-3.el7.noarch.rpm[[emailprotected] ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 2. 下载docker ce的repo [[email

  7. Zabbix结合Mojo-Webqq实现告警

    Zabbix结合Mojo-Webqq实现告警 2019独角兽企业重金招聘Python工程师标准 重要通知 WebQQ已于2019年1月1日停止服务,Mojo-Webqq项目已停止维护。 安装方法 推荐使用cpanm在线安装或升级Mojo::Webqq模块, 如果使用docker方式请参见Docker镜像安装及使用方法 1.

  8. 复古与现代的完美结合本田 Honda CB650R

    复古与现代的完美结合:本田 Honda CB650R 本田 Honda CB650R 排量:649 mL 发动机:水冷四冲程 并列四缸 长x宽x高:2140x750x1150 mm 最大功率:56/9000 kW(r/min) 最大扭矩:60/8000 Nm(r/min) 最高车速: 启动方式:电启动 点火方式:电喷 制动方式:前双