本文是【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. 【响应式编程的思维艺术】 (1)Rxjs专题学习计划

    目录 一. 响应式编程 二. 学习路径规划 一. 响应式编程 响应式编程,也称为流式编程,对于非前端工程师来说,可能并不是一个陌生的名词,它是函数式编程在软件开发中应用的延伸,如果你对函数式编程还没有 ...

  2. 【响应式编程的思维艺术】 (2)响应式Vs面向对象

    目录 一. 划重点 二. 面向对象编程实例 2.1 动画的基本编程范式 2.2 参考代码 2.3 小结 三. 响应式编程实现 四. 差异对比 4.1 编程理念差异 4.2 编程体验差异 4.3 数学思 ...

  3. 【响应式编程的思维艺术】 (3)flatMap背后的代数理论Monad

    目录 一. 划重点 二. flatMap功能解析 三. flatMap的推演 3.1 函数式编程基础知识回顾 3.2 从一个容器的例子开始 3.3 Monad登场 3.4 对比总结 3.5 一点疑问 ...

  4. ReactiveCocoa,最受欢迎的iOS函数响应式编程库(2.5版),没有之一!

    简介 项目主页: ReactiveCocoa 实例下载: https://github.com/ios122/ios122 简评: 最受欢迎,最有价值的iOS响应式编程库,没有之一!iOS MVVM模 ...

  5. Angular : 响应式编程, 组件间通信, 表单

    Angular 响应式编程相关 ------------------------------------------------------------------------------------ ...

  6. angular响应式编程

    1.响应式编程 例子import {Observable} from "rxjs/Observable"; Observable.from([1,2,3,4]) .filter(( ...

  7. Angular4学习笔记(五)- 数据绑定、响应式编程和管道

    概念 Angular中的数据绑定指的是同一组件中控制器文件(.ts)与视图文件(.html)之间的数据传递. 分类 流向 单向绑定 它的意思是要么是ts文件为html文件赋值,要么相反. ts-> ...

  8. Angular6 基础(数据绑定、生命周期、父子组件通讯、响应式编程)

    Angular相比于vue来说,更像一个完整的框架,本身就集成了很多模块,如路由,HTTP,服务等,而vue是需要另外引入比如(vuex,axios等).Angular引入了依赖注入.单元测试.类等后 ...

  9. Angular09 数据绑定、响应式编程、管道

    1 数据绑定的分类 1.1 单向数据绑定 1.1.1 属性绑定 -> 数据从组件控制类到组件模板 DOM属性绑定 HTML属性绑定 1.1.2 事件绑定 -> 数据从组件模板到组件控制类 ...

随机推荐

  1. Cocos2d-x 实战

    跨平台商业项目实战:攻城大作战游戏创意触发点:做什么样的游戏?分析当前主流的游戏:经典游戏(俄罗斯方块).大众化的游戏(卡牌游戏.休闲游戏).重口味游戏. 游戏创意:生活当中 游戏开发流程:1.策划方 ...

  2. Pycharm快捷键记录

    这里只记录自己用过的,记录而已 会慢慢添加进来,没有考虑分类和顺序,后期足够多了会整理 参考文章: 1. pycharm的一些快捷键 2. pycharm快捷键及一些常用设置 Ctrl+C  直接复制 ...

  3. tomcat启动报错:Address already in use: JVM_Bind

    tomcat启动时出现Address already in use: JVM_Bind 的原因是因为端口被占用,有可能是因为多次启动tomcat或者启动了多个tomcat,或者是其他应用程序或者服务占 ...

  4. The more,the better。

    贪婪是好的, 贪婪是对的, 贪婪是有用的, 贪婪是可以清理一切的, 贪婪是不断进化和进步的精华所在, 贪婪就是一切形式所在: 对于生活,对于爱情,对于知识我们一定要贪婪, 贪婪就是人们的动力. The ...

  5. Maven Scope 依赖范围

    Maven依赖范围就是用来控制依赖与这三种classpath(编译classpath.测试classpath.运行classpath)的关系,Maven有以下几种依赖范围: ·compile:编译依赖 ...

  6. Generator的正确打开方式

    前两年大量的在写Generator+co,用它来写一些类似同步的代码但实际上,Generator并不是被造出来干这个使的,不然也就不会有后来的async.await了Generator是一个可以被暂停 ...

  7. spring中注解式事务不生效的问题

    常用的解决方法可以百度,我针对我的问题描述一下 Mysql中InnoDB引擎才支持事务, MyISAM不支持事务. 当你尝试了各种方法解决spring中注解式事务不生效时, 一定要查看一下数据库中表的 ...

  8. CSS透明opacity和IE各版本透明度滤镜filter的准确用法

    滤镜名    说明 Alpha     让HTML元件呈现出透明的渐进效果Blur     让HTML元件产生风吹模糊的效果Chroma     让图像中的某一颜色变成透明色DropShadow    ...

  9. linux下安装python3.6.4

    想在阿里云端装一个 python36,因为自带的python2有点老 ,(centos系统) 当然你如果选择的乌班图系统的话就自带了python3,就不用看了 于是查找资料,但是一步一步的来总是不行, ...

  10. fatal: HttpRequestException encountered 解决方法

    fatal: HttpRequestException encountered解决方法   之前在windows下一段时间git push都没什么问题,最近一旦提交就会弹出  无论是push前先将远程 ...