详细介绍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 相关文章推荐
让GoogleCode的SVN下的HTML文件在FireFox下正常显示.
May 25 Javascript
LazyLoad 延迟加载(按需加载)
May 31 Javascript
html5的自定义data-*属性和jquery的data()方法的使用示例
Aug 21 Javascript
单击某一段文字改写文本颜色
Jun 06 Javascript
点击A元素触发B元素的事件在IE8下会识别成A元素
Sep 04 Javascript
js中取得变量绝对值的方法
Jan 03 Javascript
JavaScript判断字符长度、数字、Email、电话等常用判断函数分享
Apr 01 Javascript
JQuery中attr方法和removeAttr方法用法实例
May 18 Javascript
简介JavaScript中的setTime()方法的使用
Jun 11 Javascript
thinkphp标签实现bootsrtap轮播carousel实例代码
Feb 19 Javascript
JS奇技之利用scroll来监听resize详解
Jun 15 Javascript
Vuejs 2.0 子组件访问/调用父组件的方法(示例代码)
Feb 08 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
PHP详细彻底学习Smarty
2008/03/27 PHP
php获取json数据所有的节点路径
2015/05/17 PHP
Ajax实现对静态页面的文章访问统计功能示例
2016/10/10 PHP
利用php实现一周之内自动登录存储机制(cookie、session、localStorage)
2016/10/31 PHP
Zend Framework入门教程之Zend_Config组件用法详解
2016/12/09 PHP
yii2.0框架使用 beforeAction 防非法登陆的方法分析
2019/09/11 PHP
Highslide.js是一款基于js实现的网页中图片展示插件
2020/03/30 Javascript
HTA版JSMin(省略修饰语若干)基于javascript语言编写
2009/12/24 Javascript
一个分享按钮的插件使用介绍(可扩展,内附开发制作流程)
2011/09/19 Javascript
js实现单行文本向上滚动效果实例代码
2013/11/28 Javascript
JS合并数组的几种方法及优劣比较
2014/09/19 Javascript
jQuery超简单选项卡完整实例
2015/09/26 Javascript
JQuery对ASP.NET MVC数据进行更新删除
2016/07/13 Javascript
jQuery mobile在页面加载时添加加载中效果 document.ready 和window.onload执行顺序比较
2016/07/14 Javascript
JavaScript调试的多个必备小Tips
2017/01/15 Javascript
JS打开摄像头并截图上传示例
2017/02/18 Javascript
微信小程序icon组件使用详解
2018/01/31 Javascript
如何利用@angular/cli V6.0直接开发PWA应用详解
2018/05/06 Javascript
jQuery实现常见的隐藏与展示列表效果示例
2018/06/04 jQuery
小程序实现授权登陆的解决方案
2018/12/02 Javascript
JS实现移动端在线签协议功能
2019/08/22 Javascript
JS数组方法shift()、unshift()用法实例分析
2020/01/18 Javascript
Python 递归函数详解及实例
2016/12/27 Python
Python查询IP地址归属完整代码
2017/06/21 Python
Python跨文件全局变量的实现方法示例
2017/12/10 Python
Dogeared官网:在美国手工制作的珠宝
2019/08/24 全球购物
New Balance德国官方网站:购买鞋子和服装
2019/08/31 全球购物
什么是测试驱动开发(TDD)
2012/02/15 面试题
出纳员岗位职责风险
2014/03/06 职场文书
教学质量评估实施方案
2014/03/17 职场文书
公共场所标语
2014/06/30 职场文书
离婚协议书怎么写2014
2014/09/30 职场文书
工程资料员岗位职责
2015/04/13 职场文书
pytorch加载预训练模型与自己模型不匹配的解决方案
2021/05/13 Python
Matplotlib可视化之添加让统计图变得简单易懂的注释
2021/06/11 Python
如何通过简单的代码描述Angular父组件、子组件传值
2022/04/07 Javascript