【响应式编程的思维艺术】 (5)Angular中Rxjs的应用示例
本文是【Rxjs 响应式编程-第四章 构建完整的Web应用程序】这篇文章的学习笔记。
示例代码托管在:http://www.github.com/dashnowords/blogs
博客园地址:《大史住在大前端》原创博文目录
华为云社区地址:【你要的前端打怪升级指南】

一. 划重点
RxJS-DOM
原文示例中使用这个库进行DOM操作,笔者看了一下github仓库,400多星,而且相关的资料很少,所以建议理解思路即可,至于生产环境的使用还是三思吧。开发中
Rxjs几乎默认是和Angular技术栈绑定在一起的,笔者最近正在使用ionic3进行开发,本篇将对基本使用方法进行演示。冷热Observable
- 冷Observable从被订阅时就发出整个值序列
- 热Observable无论是否被订阅都会发出值,机制类似于
javascript事件。
涉及的运算符
bufferWithTime(time:number)-每隔指定时间将流中的数据以数组形式推送出去。pluck(prop:string)- 操作符,提取对象属性值,是一个柯里化后的函数,只接受一个参数。
二. Angular应用中的Http请求
Angular应用中基本HTTP请求的方式:
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { MessageService } from './message.service';//某个自定义的服务
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class HeroService {
private localhost = 'http://localhost:3001';
private all_hero_api = this.localhost + '/hero/all';//查询所有英雄
private query_hero_api = this.localhost + '/hero/query';//查询指定英雄
constructor(private http:HttpClient) {
}
/*一般get请求*/
getHeroes(): Observable<HttpResponse<Hero[]>>{
return this.http.get<Hero[]>(this.all_hero_api,{observe:'response'});
}
/*带参数的get请求*/
getHero(id: number): Observable<HttpResponse<Hero>>{
let params = new HttpParams();
params.set('id', id+'');
return this.http.get<Hero>(this.query_hero_api,{params:params,observe:'response'});
}
/*带请求体的post请求,any可以自定义响应体格式*/
createHero(newhero: object): Observable<HttpResponse<any>>{
return this.http.post<HttpResponse<any>>(this.create_hero_api,{data:newhero},{observe:'response'});
}
}
在express中写一些用于测试的虚拟数据:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/all', function(req, res, next) {
let heroes = [{
index:1,
name:'Thor',
hero:'God of Thunder'
},{
index:2,
name:'Tony',
hero:'Iron Man'
},{
index:3,
name:'Natasha',
hero:'Black Widow'
}]
res.send({
data:heroes,
result:true
})
});
/* GET home page. */
router.get('/query', function(req, res, next) {
console.log(req.query);
let hero= {
index:4,
name:'Steve',
hero:'Captain America'
}
res.send({
data:hero,
result:true
})
});
/* GET home page. */
router.post('/create', function(req, res, next) {
console.log(req.body);
let newhero = {
index:5,
name:req.body.name,
hero:'New Hero'
}
res.send({
data:newhero,
result:true
})
});
module.exports = router;
在组件中调用上面定义的方法:
sendGet(){
this.heroService.getHeroes().subscribe(resp=>{
console.log('响应信息:',resp);
console.log('响应体:',resp.body['data']);
})
}
sendQuery(){
this.heroService.getHero(1).subscribe(resp=>{
console.log('响应信息:',resp);
console.log('响应体:',resp.body['data']);
})
}
sendPost(){
this.heroService.createHero({name:'Dash'}).subscribe(resp=>{
console.log('响应信息:',resp);
console.log('响应体:',resp.body['data']);
})
}
控制台打印的信息可以看到后台的虚拟数据已经被请求到了:

三. 使用Rxjs构建Http请求结果的处理管道
3.1 基本示例
尽管看起来Http请求的返回结果是一个可观测对象,但是它却没有map方法,当需要对http请求返回的可观测对象进行操作时,可以使用pipe操作符来实现:
import { Observable, of, from} from 'rxjs';
import { map , tap, filter, flatMap }from 'rxjs/operators';
/*构建一个模拟的结果处理管道
*map操作来获取数据
*tap实现日志
*flatMap实现结果自动遍历
*filter实现结果过滤
*/
getHeroes$(): Observable<HttpResponse<Hero[]>>{
return this.http.get<Hero[]>(this.all_hero_api,{observe:'response'})
.pipe(
map(resp=>resp.body['data']),
tap(this.log),
flatMap((data)=>{return from(data)}),
filter((data)=>data['index'] > 1)
);
}
很熟悉吧?经过处理管道后,一次响应中的结果数据被转换为逐个发出的数据,并过滤掉了不符合条件的项:

3.2 常见的操作符
Angular中文网列举了最常用的一些操作符,RxJS官方文档有非常详细的示例及说明,且均配有形象的大理石图,建议先整体浏览一下有个印象,有需要的读者可以每天熟悉几个,很快就能上手,运算符的使用稍显抽象,且不同运算符的组合使用在流程控制和数据处理方面的用法灵活多变,也是有很多套路的,开发经验需要慢慢积累。

四. 冷热Observable的两种典型场景
原文中提到的冷热Observable的差别可以参考这篇文章【RxJS:冷热模式的比较】,概念本身并不难理解。
4.1 shareReplay与请求缓存
开发中常会遇到这样一种场景,某些集合型的常量,完全是可以复用的,通常开发者会将其进行缓存至某个全局单例中,接着在优化阶段,通过增加一个if判断在请求之前先检查缓存再决定是否需要请求,Rxjs提供了一种更优雅的实现。
先回顾一下上面的http请求代码:
getHeroes(): Observable<HttpResponse<Hero[]>>{
return this.http.get<Hero[]>(this.all_hero_api,{observe:'response'});
}
http请求默认返回一个冷Observable,每当返回的流被订阅时就会触发一个新的http请求,Rxjs中通过shareReplay( )操作符将一个可观测对象转换为热Observable(注意:shareReplay( )不是唯一一种可以加热Observable的方法),这样在第一次被订阅时,网络请求被发出并进行了缓存,之后再有其他订阅者加入时,就会得到之前缓存的数据,运算符的名称已经很清晰了,【share-共享】,【replay-重播】,是不是形象又好记。对上面的流进行一下转换:
getHeroes$(): Observable<HttpResponse<Hero[]>>{
return this.http.get<Hero[]>(this.all_hero_api,{observe:'response'})
.pipe(
map(resp=>resp.body['data']),
tap(this.log),
flatMap((data)=>{return from(data)}),
filter((data)=>data['index'] > 1),
shareReplay() // 转换管道的最后将这个流转换为一个热Observable
)
}
在调用的地方编写调用代码:
sendGet(){
let obs = this.heroService.getHeroes$();
//第一次被订阅
obs.subscribe(resp=>{
console.log('响应信息:',resp);
});
//第二次被订阅
setTimeout(()=>{
obs.subscribe((resp)=>{
console.log('延迟后的响应信息',resp);
})
},2000)
}
通过结果可以看出,第二次订阅没有触发网络请求,但是也得到了数据:

网络请求只发送了一次(之前的会发送两次):

4.2 share与异步管道
这种场景笔者并没有进行生产实践,一是因为这种模式需要将数据的变换处理全部通过pipe( )管道来进行,笔者自己的函数式编程功底可能还不足以应付,二来总觉得很多示例的使用场景很牵强,所以仅作基本功能介绍,后续有实战心得后再修订补充。Angular中提供了一种叫做异步管道的模板语法,可以直接在*ngFor的微语法中使用可观测对象:
<ul>
<li *ngFor="let contact of contacts | async">{{contact.name}}</li>
</ul>
<ul>
<li *ngFor="let contact of contacts2 | async">{{contact.name}}</li>
</ul>
示例:
this.contacts = http.get('contacts.json')
.map(response => response.json().items)
.share();
setTimeout(() => this.contacts2 = this.contacts, 500);
五. 一点建议
一定要好好读官方文档。
【响应式编程的思维艺术】 (5)Angular中Rxjs的应用示例的更多相关文章
- 【响应式编程的思维艺术】 (1)Rxjs专题学习计划
目录 一. 响应式编程 二. 学习路径规划 一. 响应式编程 响应式编程,也称为流式编程,对于非前端工程师来说,可能并不是一个陌生的名词,它是函数式编程在软件开发中应用的延伸,如果你对函数式编程还没有 ...
- 【响应式编程的思维艺术】 (2)响应式Vs面向对象
目录 一. 划重点 二. 面向对象编程实例 2.1 动画的基本编程范式 2.2 参考代码 2.3 小结 三. 响应式编程实现 四. 差异对比 4.1 编程理念差异 4.2 编程体验差异 4.3 数学思 ...
- 【响应式编程的思维艺术】 (3)flatMap背后的代数理论Monad
目录 一. 划重点 二. flatMap功能解析 三. flatMap的推演 3.1 函数式编程基础知识回顾 3.2 从一个容器的例子开始 3.3 Monad登场 3.4 对比总结 3.5 一点疑问 ...
- ReactiveCocoa,最受欢迎的iOS函数响应式编程库(2.5版),没有之一!
简介 项目主页: ReactiveCocoa 实例下载: https://github.com/ios122/ios122 简评: 最受欢迎,最有价值的iOS响应式编程库,没有之一!iOS MVVM模 ...
- Angular : 响应式编程, 组件间通信, 表单
Angular 响应式编程相关 ------------------------------------------------------------------------------------ ...
- angular响应式编程
1.响应式编程 例子import {Observable} from "rxjs/Observable"; Observable.from([1,2,3,4]) .filter(( ...
- Angular4学习笔记(五)- 数据绑定、响应式编程和管道
概念 Angular中的数据绑定指的是同一组件中控制器文件(.ts)与视图文件(.html)之间的数据传递. 分类 流向 单向绑定 它的意思是要么是ts文件为html文件赋值,要么相反. ts-> ...
- Angular6 基础(数据绑定、生命周期、父子组件通讯、响应式编程)
Angular相比于vue来说,更像一个完整的框架,本身就集成了很多模块,如路由,HTTP,服务等,而vue是需要另外引入比如(vuex,axios等).Angular引入了依赖注入.单元测试.类等后 ...
- Angular09 数据绑定、响应式编程、管道
1 数据绑定的分类 1.1 单向数据绑定 1.1.1 属性绑定 -> 数据从组件控制类到组件模板 DOM属性绑定 HTML属性绑定 1.1.2 事件绑定 -> 数据从组件模板到组件控制类 ...
随机推荐
- Java MD5加密与RSA加密
区别: MD5加密: 加密时通过原字符串加密成另一串字符串 解密时需要原加密字符串进行重新加密比较两次加密结果是否一致 T=RSA加密: 加密时通过原字符串生成密钥对(公钥+私钥) 解密时通过公钥和私 ...
- Docker容器发布spring boot项目
一.安装Docker环境 yum install docker 安装完成后,使用下面的命令来启动 docker 服务,并将其设置为开机启动: systemctl start docker.servic ...
- 基于Kafka Connect框架DataPipeline可以更好地解决哪些企业数据集成难题?
DataPipeline已经完成了很多优化和提升工作,可以很好地解决当前企业数据集成面临的很多核心难题. 1. 任务的独立性与全局性. 从Kafka设计之初,就遵从从源端到目的的解耦性.下游可以有很多 ...
- Linux Vim配置
用过很多vim配置的版本,怎么说,想轻量级就不要胡加乱七八糟的功能:如果不在乎反应是不是快速,侧重功能是否强大,可以参考vim大神的配置策略(spf13-vim)https://github.com/ ...
- Spring Cloud Sleuth服务链路追踪(zipkin)(转)
这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件. 一.简介 Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案, ...
- SSM-SpringMVC-16:SpringMVC中小论注解式开发之访问方式篇
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 访问方式可以指定,打个比方,你通过get方式进入登陆页面,通过post发送ajax数据库校验或者post提交 ...
- js算法初窥02(排序算法02-归并、快速以及堆排序)
上一篇,我们讲述了一些简单的排序算法,其实说到底,在前端的职业生涯中,不涉及node.不涉及后台的情况下,我目前还真的没想到有哪些地方可以用到这些数据结构和算法,但是我在前面的文章也说过了.或许你用不 ...
- poj 1696 极角排序求最长逆时针螺旋线
Space Ant Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 4970 Accepted: 3100 Descrip ...
- tkinter中scale拖拉改变值控件(十一)
scale拖拉改变值控件 使用户通过拖拽改变值 简单的实现: import tkinter wuya = tkinter.Tk() wuya.title("wuya") wuya. ...
- 你不知道的JavaScript--Item21 漂移的this
而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就导致 JavaScript 中的 this 关键字有能力具备多重含义,带来灵活性的同时,也为初学者带来不少困惑.本文仅就这 ...