详细介绍RxJS在Angular中的应用


Posted in Javascript onSeptember 23, 2017

RxJS是一种针对异步数据流编程工具,或者叫响应式扩展编程;可不管如何解释RxJS其目标就是异步编程,Angular引入RxJS为了就是让异步可控、更简单。

而今就是要探讨什么是Observable、observer、operator、Submit、EventEmmit,以及如何去使用它们。

什么是Observable?

Observable只是一个普通函数,要想让他有所作为,就需要跟observer一起使用;前者是受后者是攻。而这个observer(后面我们会介绍)只是一个带有 next、error、complete 的简单对象而已。最后,还需要通过 subscribe 订阅来启动Observable;否则它是不会有任何反应;可以理解为陌*为了他们能在一起而提供的环境,而订阅也会返回一个可用于取消操作(在RxJS里叫 unsubscribe)。

当Observable设置观察者后,而连接并获取原始数据的这个过程叫生产者,可能是DOM中的 click 事件、input 事件、或者更加复杂的HTTP通信。

为了更好理解,先从一个简单的示例开始:

import { Component } from '@angular/core';
import { Observable, Subscription } from 'rxjs';

@Component({
 selector: 'app-home',
 template: `<input type="text"> `
})
export class HomeComponent {
 ngOnInit() {
  const node = document.querySelector('input[type=text]');

  // 第二个参数 input 是事件名,对于input元素有一个 oninput 事件用于接受用户输入
  const input$ = Observable.fromEvent(node, 'input');
  input$.subscribe({
   next: (event: any) => console.log(`You just typed ${event.target.value}!`),
   error: (err) => console.log(`Oops... ${err}`),
   complete: () => console.log(`Complete!`)
  });
 }
}

示例中 Observable.fromEvent() 会返回一个Observable,并且监听 input 事件,当事件被触发后会发送一个 Event 给对应的observer观察者。

什么是observer?

observer非常简单,像上面示例中 subscribe 订阅就是接收一个 observer 方法。

一般在Angular我们 subscribe 会这么写:

input$.subscribe((event: any) => {

});

从语法角度来讲和 subscribe({ next, error, complete }) 是一样的。

当Observable产生一个新值时,会通知 observer 的 next(),而当捕获失败可以调用 error()。

当Observable被订阅后,除非调用observer的 complete() 或 unsubscribe() 取消订阅两情况以外;会一直将值传递给 observer。

Observable的生产的值允许经过一序列格式化或操作,最终得到一个有价值的数据给观察者,而这一切是由一序列链式operator来完成的,每一个operator都会产生一个新的Observable。而我们也称这一序列过程为:流。

什么是operator?

正如前面说到的,Observable可以链式写法,这意味着我们可以这样:

Observable.fromEvent(node, 'input')
 .map((event: any) => event.target.value)
 .filter(value => value.length >= 2)
 .subscribe(value => { console.log(value); });

下面是整个顺序步骤:

  • 假设用户输入:a
  • Observable对触发 oninput 事件作出反应,将值以参数的形式传递给observer的 next()。
  • map() 根据 event.target.value 的内容返回一个新的 Observable,并调用 next() 传递给下一个observer。
  • filter() 如果值长度 >=2 的话,则返回一个新的 Observable,并调用 next() 传递给下一个observer。
  • 最后,将结果传递给 subscribe 订阅块。

你只要记住每一次 operator 都会返回一个新的 Observable,不管 operator 有多少个,最终只有最后一个 Observable 会被订阅。

不要忘记取消订阅

为什么需要取消订阅

Observable 当有数据产生时才会推送给订阅者,所以它可能会无限次向订阅者推送数据。正因为如此,在Angular里面创建组件的时候务必要取消订阅操作,以避免内存泄漏,要知道在SPA世界里懂得擦屁股是一件必须的事。

unsubscribe

前面示例讲过,调用 subscribe() 后,会返回一个 Subscription 可用于取消操作 unsubscribe()。最合理的方式在 ngOnDestroy 调用它。

ngOnDestroy() {
  this.inputSubscription.unsubscribe();
}

takeWhile

如果组件有很多订阅者的话,则需要将这些订阅者存储在数组中,并组件被销毁时再逐个取消订阅。但,我们有更好的办法:

使用 takeWhile() operator,它会在你传递一个布尔值是调用 next() 还是 complete()。

private alive: boolean = true;
ngOnInit() {
 const node = document.querySelector('input[type=text]');

 this.s = Observable.fromEvent(node, 'input')
  .takeWhile(() => this.alive)
  .map((event: any) => event.target.value)
  .filter(value => value.length >= 2)
  .subscribe(value => { console.log(value) });
}

ngOnDestroy() {
 this.alive = false;
}

简单有效,而且优雅。

Subject

如果说 Observable 与 observer 是攻受结合体的话,那么 Subject 就是一个人即攻亦受。正因为如此,我们在写一个Service用于数据传递时,总是使用 new Subject。

@Injectable()
export class MessageService {
  private subject = new Subject<any>();

  send(message: any) {
    this.subject.next(message);
  }

  get(): Observable<any> {
    return this.subject.asObservable();
  }
}

当F组件需要向M组件传递数据时,我们可以在F组件中使用 send()。

constructor(public srv: MessageService) { }

ngOnInit() {
  this.srv.send('w s k f m?')
}

而M组件只需要订阅内容就行:

constructor(private srv: MessageService) {}

message: any;
ngOnInit() {
  this.srv.get().subscribe((result) => {
    this.message = result;
  })
}

EventEmitter

其实EventEmitter跟RxJS没有直接关系,因为他是Angular的产物,而非RxJS的东西。或者我们压根没必要去谈,因为EventEmitter就是Subject。

EventEmitter的作用是使指令或组件能自定义事件。

@Output() changed = new EventEmitter<string>();

click() {
  this.changed.emit('hi~');
}

@Component({
 template: `<comp (changed)="subscribe($event)"></comp>`
})
export class HomeComponent {
 subscribe(message: string) {
   // 接收:hi~
 }
}

上面示例其实和上一个示例中 MessageService 如出一辙,只不过是将 next() 换成 emit() 仅此而已。

结论

RxJS最难我想就是各种operator的应用了,这需要一些经验的积累。

RxJS很火很大原因我认还是提供了丰富的API,以下是摘抄:

创建数据流:

  • 单值:of, empty, never
  • 多值:from
  • 定时:interval, timer
  • 从事件创建:fromEvent
  • 从Promise创建:fromPromise
  • 自定义创建:create

转换操作:

  • 改变数据形态:map, mapTo, pluck
  • 过滤一些值:filter, skip, first, last, take
  • 时间轴上的操作:delay, timeout, throttle, debounce, audit, bufferTime
  • 累加:reduce, scan
  • 异常处理:throw, catch, retry, finally
  • 条件执行:takeUntil, delayWhen, retryWhen, subscribeOn, ObserveOn
  • 转接:switch

组合数据流:

  • concat,保持原来的序列顺序连接两个数据流
  • merge,合并序列
  • race,预设条件为其中一个数据流完成
  • forkJoin,预设条件为所有数据流都完成
  • zip,取各来源数据流最后一个值合并为对象
  • combineLatest,取各来源数据流最后一个值合并为数组

另,最好使用 $ 结尾的命名方式来表示Observable,例:input$。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js判断选择的时间是否大于今天的代码
Aug 20 Javascript
使用原生js封装webapp滑动效果(惯性滑动、滑动回弹)
May 06 Javascript
使用JavaScript实现连续滚动字幕效果的方法
Jul 07 Javascript
jQuery中DOM节点的删除方法总结(超全面)
Jan 22 Javascript
Vue2组件tree实现无限级树形菜单
Mar 29 Javascript
详解如何在Vue2中实现组件props双向绑定
Mar 29 Javascript
纯js实现隔行变色效果
Nov 29 Javascript
React Native 截屏组件的示例代码
Dec 06 Javascript
vue2.0在没有dev-server.js下的本地数据配置方法
Feb 23 Javascript
jQuery实现表单动态添加数据并提交的方法
Jul 19 jQuery
微信小程序实现下拉框功能
Jul 16 Javascript
ES11屡试不爽的新特性,你用上了几个
Oct 21 Javascript
Javascript刷新页面的实例
Sep 23 #Javascript
react-native-fs实现文件下载、文本存储的示例代码
Sep 22 #Javascript
jQuery实现table中两列CheckBox只能选中一个的示例
Sep 22 #jQuery
解决html-jquery/js引用外部图片时遇到看不了或出现403的问题
Sep 22 #jQuery
layer子层给父层页面元素赋值,以达到向父层页面传值的效果实例
Sep 22 #Javascript
js表单序列化判断空值的实例
Sep 22 #Javascript
VUE中的无限循环代码解析
Sep 22 #Javascript
You might like
星际争霸 Starcraft 发展史
2020/03/14 星际争霸
PHP Swoole异步Redis客户端实现方法示例
2019/10/24 PHP
js优化针对IE6.0起作用(详细整理)
2012/12/25 Javascript
轻量级的原生js日历插件calendar.js使用指南
2015/04/28 Javascript
jquery带动画效果幻灯片特效代码
2015/08/27 Javascript
Node.js返回JSONP详解
2016/05/18 Javascript
AngularJs 延时器、计时器实例代码
2017/09/16 Javascript
JS函数节流和函数防抖问题分析
2017/12/18 Javascript
详解在React.js中使用PureComponent的重要性和使用方式
2018/07/10 Javascript
小程序按钮避免多次调用接口和点击方案实现(不用showLoading)
2020/04/15 Javascript
微信小程序中使用 async/await的方法实例分析
2020/05/06 Javascript
[01:19:34]2014 DOTA2国际邀请赛中国区预选赛 New Element VS Dream time
2014/05/22 DOTA
Python中关键字is与==的区别简述
2014/07/31 Python
python使用Flask框架获取用户IP地址的方法
2015/03/21 Python
CentOS 6.5下安装Python 3.5.2(与Python2并存)
2017/06/05 Python
详解python string类型 bytes类型 bytearray类型
2017/12/16 Python
python实现列表中最大最小值输出的示例
2019/07/09 Python
Django中间件基础用法详解
2019/07/18 Python
Python3 用matplotlib绘制sigmoid函数的案例
2020/12/11 Python
Canvas制作的下雨动画的示例
2018/03/06 HTML / CSS
写clone()方法时,通常都有一行代码,是什么?
2012/10/31 面试题
中级会计职业生涯规划范文
2014/01/16 职场文书
中英文求职信范文
2014/01/27 职场文书
怎么写好自荐书
2014/03/02 职场文书
关于中国梦的演讲稿
2014/04/23 职场文书
任命书模板
2014/06/04 职场文书
军训拉歌口号
2014/06/13 职场文书
应届大学生自荐书
2014/06/17 职场文书
离职证明标准格式
2014/09/15 职场文书
乡镇组织委员个人整改措施
2014/09/16 职场文书
2014年局领导班子自身建设情况汇报
2014/11/21 职场文书
北京颐和园导游词
2015/01/30 职场文书
小学生交通安全寄语
2015/02/27 职场文书
沂蒙六姐妹观后感
2015/06/08 职场文书
Python re.sub 反向引用的实现
2021/07/07 Python
Nginx+Tomcat负载均衡集群的实现示例
2021/10/24 Servers