详细介绍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 相关文章推荐
JScript中的&quot;this&quot;关键字使用方式补充材料
Mar 08 Javascript
jquery动态加载图片数据练习代码
Aug 04 Javascript
JavaScript验证图片类型(扩展名)的函数分享
May 05 Javascript
Javascript中实现String.startsWith和endsWith方法
Jun 10 Javascript
JavaScript实现广告的关闭与显示效果实例
Jul 02 Javascript
使用基于Node.js的构建工具Grunt来发布ASP.NET MVC项目
Feb 15 Javascript
JavaScript中的Array 对象(数组对象)
Jun 02 Javascript
jQuery3.0中的buildFragment私有函数详解
Aug 16 Javascript
vue-lazyload图片延迟加载插件的实例讲解
Feb 09 Javascript
jQuery实现input输入框获取焦点与失去焦点时提示的消失与显示功能示例
May 27 jQuery
微信小程序用户拒绝授权的处理方法详解
Sep 20 Javascript
ES6 Iterator遍历器原理,应用场景及相关常用知识拓展详解
Feb 15 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
destoon利用Rewrite规则设置网站安全
2014/06/21 PHP
PHP6新特性分析
2016/03/03 PHP
PHP中类属性与类静态变量的访问方法示例
2016/07/13 PHP
select 控制网页内容隐藏于显示的实现代码
2010/05/25 Javascript
基于Jquery的跨域传输数据(JSONP)
2011/03/10 Javascript
常用Extjs工具:Extjs.util.Format使用方法
2012/03/22 Javascript
javascript利用apply和arguments复用方法
2013/11/25 Javascript
使用JavaScript进行进制转换将字符串转换为十进制
2014/09/21 Javascript
node.js中的favicon.ico请求问题处理
2014/12/15 Javascript
自定义刻度jQuery进度条及插件
2015/09/02 Javascript
JS模拟的Map类实现方法
2016/06/17 Javascript
Vue.js每天必学之指令系统与自定义指令
2016/09/07 Javascript
arcgis for js 修改infowindow样式的方法
2016/11/02 Javascript
详解easyui 切换主题皮肤
2019/04/04 Javascript
微信小程序实现判断是分享到群还是个人功能示例
2019/05/03 Javascript
JavaScript实现简单的计算器
2020/01/16 Javascript
Vue双向绑定实现原理与方法详解
2020/05/07 Javascript
Vue切换组件实现返回后不重置数据,保留历史设置操作
2020/07/21 Javascript
[00:58]PWL开团时刻DAY5——十人开雾0换5
2020/11/04 DOTA
Python的Django框架中TEMPLATES项的设置教程
2015/05/29 Python
Python字符串、元组、列表、字典互相转换的方法
2016/01/23 Python
深入理解python多进程编程
2016/06/12 Python
python生成器与迭代器详解
2019/01/01 Python
对python条件表达式的四种实现方法小结
2019/01/30 Python
django 做 migrate 时 表已存在的处理方法
2019/08/31 Python
python 实现表情识别
2020/11/21 Python
Pycharm 设置默认解释器路径和编码格式的操作
2021/02/05 Python
详解HTML5中的picture元素响应式处理图片
2018/01/03 HTML / CSS
全球速卖通:AliExpress(国际版淘宝)
2017/09/20 全球购物
用友笔试题目
2016/10/25 面试题
介绍一下RMI的基本概念
2016/12/17 面试题
应届生服务员求职信
2013/10/31 职场文书
曾国藩励志经典名言37句,蕴含哲理
2019/10/14 职场文书
PyTorch dropout设置训练和测试模式的实现
2021/05/27 Python
分享3个非常实用的 Python 模块
2022/03/03 Python
手写Spirit防抖函数underscore和节流函数lodash
2022/03/22 Javascript