本文是【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. hadoop中setup,cleanup,run和context讲解

    hadoop 执行中的setup run cleanup context的作用1.简介1) setup(),此方法被MapReduce框架仅且执行一次,在执行Map任务前,进行相关变量或者资源的集中初 ...

  2. 初入thinkphp

    花3天时间入门了php和thinkphp框架,紧接着就做了一个小后台,简单使用了thinkphp框架封装的一些类和函数. 现在来总结一下:             //登陆函数 public func ...

  3. android sqlite android.database.CursorIndexOutOfBoundsException: Index 5 requested, with a size of 5

    Cursor c = db.query("user",null,null,null,null,null,null);//查询并获得游标 if(c.moveToFirst()){// ...

  4. GPU渲染流水线的简单概括

    GPU流水线 主要分为两个阶段:几何阶段和光栅化阶段   几何阶段      顶点着色器 --> 曲面细分着色器(可选)----->几何着色器(可选)----->裁剪-->屏幕 ...

  5. PAT1056:Mice and Rice

    1056. Mice and Rice (25) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Mice an ...

  6. SSM-SpringMVC-04:SpringMVC深入浅出理解HandleMapping(源码刨析)

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 先从概念理解,从中央调度器,携带参数request,调度到HandleMapping处理器映射器,处理器映射器 ...

  7. 给xmpphp添加了几个常用的方法

    给xmpphp添加给了以下的常用方法: registerNewUser            //注册一个新用户 addRosterContact           //发送添加好友的请求 acce ...

  8. Java中的Unsafe类111

    1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...

  9. classpath和filepath

    ******************************** java中的相对路径和绝对路径 ******************************** 相对路径(其实就是编译后的路径) - ...

  10. log.go

    ) //打开日志文件 以及文件操作权限     if err != nil {         return err     }     // 解析日志记录的等级信息     level, err : ...