本文是【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. GitHub学习笔记:分支管理

    GitHub对于每个开发版本都需要有一个分支,默认的分支是master往往被大家保留下来作为主分支,分支类似于进程的一个指针,往往在master这个稳定的主干版本上分出一个或多个正在开发的分支版本,开 ...

  2. Django REST framework+Vue 打造生鲜超市(十三)

    目录 生鲜超市(一)    生鲜超市(二)    生鲜超市(三) 生鲜超市(四)    生鲜超市(五)    生鲜超市(六) 生鲜超市(七)    生鲜超市(八)    生鲜超市(九) 生鲜超市(十) ...

  3. SQL Server性能优化(8)堆表结构介绍

    一.表结构综述 下图是SQL Server中表的组织形式(其中分区1.分区2是为了便于管理,把表进行分区,放到不同的硬盘数据文件里.默认情况下,表只有一个分区.).表在硬盘上的存放形式,有堆和B树两种 ...

  4. JaveScript基础(2)之数据类型转换和常用字符串的操作方法

    1.JaveScript数据类型转换: A.转字符串:通过"+"或toString(); PS:如果都是数值类型,'+'会进行求和运算,否则会做字符串连接: var s=2.5;d ...

  5. Diffie-Hellman密钥协商算法

    一.概述 Diffie-Hellman密钥协商算法主要解决秘钥配送问题,本身并非用来加密用的:该算法其背后有对应数学理论做支撑,简单来讲就是构造一个复杂的计算难题,使得对该问题的求解在现实的时间内无法 ...

  6. PAT1070:Mooncake

    1070. Mooncake (25) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Mooncake is ...

  7. https://doc.opensuse.org/projects/kiwi/doc/

    KIWI 是用于创建操作系统映像的系统.映像是带有一个文件的目录,该文件包含操作系统.其应用程序与配置.操作系统的文件系统结构.可能的附加元数据,以及(取决于映像类型)磁盘几何属性和分区表数据.通过 ...

  8. Oracle-02:SQL语言的分类或者说SQL语言的组成

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 小结一版:  01.DDL(Data Definition Language)数据定义语言. 用来创建数据库中 ...

  9. 传统IT公司/创业公司/互联网大公司的offer如何选择?[转载+原创]

    背景介绍: 第一家工作的公司是一家跨国外企安全公司, 骄傲的称自己不是互联网公司而是传统软件公司, 第二家公司是当下最热的知识分享社区, 创业公司. 第三家公司是挤走谷歌, 曾一度称霸中国的搜索引擎公 ...

  10. easyui下载包详解

    easyui包详解: 文件夹: demo--该目录下存放的是 EasyUI PC 版各插件的示例示例.如果不想在官网上查看演示,可以在该目录下找到相应的演示示例 demo-mobile--该目录下存放 ...