资讯专栏INFORMATION COLUMN

【响应式编程的思维艺术】 (5)Angular中Rxjs的应用示例

shenhualong / 3232人阅读

摘要:本文是响应式编程第四章构建完整的应用程序这篇文章的学习笔记。涉及的运算符每隔指定时间将流中的数据以数组形式推送出去。中提供了一种叫做异步管道的模板语法,可以直接在的微语法中使用可观测对象示例五一点建议一定要好好读官方文档。

</>复制代码

  1. 本文是【Rxjs 响应式编程-第四章 构建完整的Web应用程序】这篇文章的学习笔记。

    示例代码托管在:http://www.github.com/dashnowords/blogs

  2. 博客园地址:《大史住在大前端》原创博文目录

  3. 华为云社区地址:【你要的前端打怪升级指南】

[TOC]

一. 划重点

RxJS-DOM

原文示例中使用这个库进行DOM操作,笔者看了一下github仓库,400多星,而且相关的资料很少,所以建议理解思路即可,至于生产环境的使用还是三思吧。开发中Rxjs几乎默认是和Angular技术栈绑定在一起的,笔者最近正在使用ionic3进行开发,本篇将对基本使用方法进行演示。

冷热Observable

冷Observable从被订阅时就发出整个值序列

热Observable无论是否被订阅都会发出值,机制类似于javascript事件。

涉及的运算符

bufferWithTime(time:number)-每隔指定时间将流中的数据以数组形式推送出去。

pluck(prop:string)- 操作符,提取对象属性值,是一个柯里化后的函数,只接受一个参数。

二. Angular应用中的Http请求

Angular应用中基本HTTP请求的方式:

</>复制代码

  1. import { Injectable } from "@angular/core";
  2. import { Observable, of } from "rxjs";
  3. import { MessageService } from "./message.service";//某个自定义的服务
  4. import { HttpClient, HttpParams, HttpResponse } from "@angular/common/http";
  5. @Injectable({
  6. providedIn: "root"
  7. })
  8. export class HeroService {
  9. private localhost = "http://localhost:3001";
  10. private all_hero_api = this.localhost + "/hero/all";//查询所有英雄
  11. private query_hero_api = this.localhost + "/hero/query";//查询指定英雄
  12. constructor(private http:HttpClient) {
  13. }
  14. /*一般get请求*/
  15. getHeroes(): Observable>{
  16. return this.http.get(this.all_hero_api,{observe:"response"});
  17. }
  18. /*带参数的get请求*/
  19. getHero(id: number): Observable>{
  20. let params = new HttpParams();
  21. params.set("id", id+"");
  22. return this.http.get(this.query_hero_api,{params:params,observe:"response"});
  23. }
  24. /*带请求体的post请求,any可以自定义响应体格式*/
  25. createHero(newhero: object): Observable>{
  26. return this.http.post>(this.create_hero_api,{data:newhero},{observe:"response"});
  27. }
  28. }

express中写一些用于测试的虚拟数据:

</>复制代码

  1. var express = require("express");
  2. var router = express.Router();
  3. /* GET home page. */
  4. router.get("/all", function(req, res, next) {
  5. let heroes = [{
  6. index:1,
  7. name:"Thor",
  8. hero:"God of Thunder"
  9. },{
  10. index:2,
  11. name:"Tony",
  12. hero:"Iron Man"
  13. },{
  14. index:3,
  15. name:"Natasha",
  16. hero:"Black Widow"
  17. }]
  18. res.send({
  19. data:heroes,
  20. result:true
  21. })
  22. });
  23. /* GET home page. */
  24. router.get("/query", function(req, res, next) {
  25. console.log(req.query);
  26. let hero= {
  27. index:4,
  28. name:"Steve",
  29. hero:"Captain America"
  30. }
  31. res.send({
  32. data:hero,
  33. result:true
  34. })
  35. });
  36. /* GET home page. */
  37. router.post("/create", function(req, res, next) {
  38. console.log(req.body);
  39. let newhero = {
  40. index:5,
  41. name:req.body.name,
  42. hero:"New Hero"
  43. }
  44. res.send({
  45. data:newhero,
  46. result:true
  47. })
  48. });
  49. module.exports = router;

在组件中调用上面定义的方法:

</>复制代码

  1. sendGet(){
  2. this.heroService.getHeroes().subscribe(resp=>{
  3. console.log("响应信息:",resp);
  4. console.log("响应体:",resp.body["data"]);
  5. })
  6. }
  7. sendQuery(){
  8. this.heroService.getHero(1).subscribe(resp=>{
  9. console.log("响应信息:",resp);
  10. console.log("响应体:",resp.body["data"]);
  11. })
  12. }
  13. sendPost(){
  14. this.heroService.createHero({name:"Dash"}).subscribe(resp=>{
  15. console.log("响应信息:",resp);
  16. console.log("响应体:",resp.body["data"]);
  17. })
  18. }

控制台打印的信息可以看到后台的虚拟数据已经被请求到了:

三. 使用Rxjs构建Http请求结果的处理管道 3.1 基本示例

尽管看起来Http请求的返回结果是一个可观测对象,但是它却没有map方法,当需要对http请求返回的可观测对象进行操作时,可以使用pipe操作符来实现:

</>复制代码

  1. import { Observable, of, from} from "rxjs";
  2. import { map , tap, filter, flatMap }from "rxjs/operators";
  3. /*构建一个模拟的结果处理管道
  4. *map操作来获取数据
  5. *tap实现日志
  6. *flatMap实现结果自动遍历
  7. *filter实现结果过滤
  8. */
  9. getHeroes$(): Observable>{
  10. return this.http.get(this.all_hero_api,{observe:"response"})
  11. .pipe(
  12. map(resp=>resp.body["data"]),
  13. tap(this.log),
  14. flatMap((data)=>{return from(data)}),
  15. filter((data)=>data["index"] > 1)
  16. );
  17. }

很熟悉吧?经过处理管道后,一次响应中的结果数据被转换为逐个发出的数据,并过滤掉了不符合条件的项:

3.2 常见的操作符

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

四. 冷热Observable的两种典型场景

原文中提到的冷热Observable的差别可以参考这篇文章【RxJS:冷热模式的比较】,概念本身并不难理解。

4.1 shareReplay与请求缓存

开发中常会遇到这样一种场景,某些集合型的常量,完全是可以复用的,通常开发者会将其进行缓存至某个全局单例中,接着在优化阶段,通过增加一个if判断在请求之前先检查缓存再决定是否需要请求,Rxjs提供了一种更优雅的实现。

先回顾一下上面的http请求代码:

</>复制代码

  1. getHeroes(): Observable>{
  2. return this.http.get(this.all_hero_api,{observe:"response"});
  3. }

http请求默认返回一个冷Observable,每当返回的流被订阅时就会触发一个新的http请求,Rxjs中通过shareReplay( )操作符将一个可观测对象转换为热Observable(注意:shareReplay( )不是唯一一种可以加热Observable的方法),这样在第一次被订阅时,网络请求被发出并进行了缓存,之后再有其他订阅者加入时,就会得到之前缓存的数据,运算符的名称已经很清晰了,【share-共享】,【replay-重播】,是不是形象又好记。对上面的流进行一下转换:

</>复制代码

  1. getHeroes$(): Observable>{
  2. return this.http.get(this.all_hero_api,{observe:"response"})
  3. .pipe(
  4. map(resp=>resp.body["data"]),
  5. tap(this.log),
  6. flatMap((data)=>{return from(data)}),
  7. filter((data)=>data["index"] > 1),
  8. shareReplay() // 转换管道的最后将这个流转换为一个热Observable
  9. )
  10. }

在调用的地方编写调用代码:

</>复制代码

  1. sendGet(){
  2. let obs = this.heroService.getHeroes$();
  3. //第一次被订阅
  4. obs.subscribe(resp=>{
  5. console.log("响应信息:",resp);
  6. });
  7. //第二次被订阅
  8. setTimeout(()=>{
  9. obs.subscribe((resp)=>{
  10. console.log("延迟后的响应信息",resp);
  11. })
  12. },2000)
  13. }

通过结果可以看出,第二次订阅没有触发网络请求,但是也得到了数据:

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

4.2 share与异步管道

这种场景笔者并没有进行生产实践,一是因为这种模式需要将数据的变换处理全部通过pipe( )管道来进行,笔者自己的函数式编程功底可能还不足以应付,二来总觉得很多示例的使用场景很牵强,所以仅作基本功能介绍,后续有实战心得后再修订补充。Angular中提供了一种叫做异步管道的模板语法,可以直接在*ngFor的微语法中使用可观测对象:

</>复制代码

    • {{contact.name}}
    • {{contact.name}}

示例:

</>复制代码

  1. this.contacts = http.get("contacts.json")
  2. .map(response => response.json().items)
  3. .share();
  4. setTimeout(() => this.contacts2 = this.contacts, 500);
五. 一点建议

一定要好好读官方文档。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110248.html

相关文章

  • 响应编程思维艺术】 (1)Rxjs专题学习计划

    摘要:由于技术栈的学习,笔者需要在原来函数式编程知识的基础上,学习的使用。笔者在社区发现了一个非常高质量的响应式编程系列教程共篇,从基础概念到实际应用讲解的非常详细,有大量直观的大理石图来辅助理解流的处理,对培养响应式编程的思维方式有很大帮助。 showImg(https://segmentfault.com/img/bVus8n); [TOC] 一. 响应式编程 响应式编程,也称为流式编程...

    lscho 评论0 收藏0
  • 响应编程思维艺术】 (2)响应Vs面向对象

    摘要:本文是响应式编程第一章响应式这篇文章的学习笔记。通过代码对比可以发现,在响应式编程中,我们不再用对象的概念来对现实世界进行建模,而是使用流的思想对信息进行拆分和聚合。 本文是Rxjs 响应式编程-第一章:响应式这篇文章的学习笔记。示例代码地址:【示例代码】 更多文章:【《大史住在大前端》博文集目录】 showImg(https://segmentfault.com/img/bVbuE...

    Tonny 评论0 收藏0
  • 响应编程思维艺术】 (3)flatMap背后代数理论Monad

    摘要:本文是响应式编程第二章序列的深入研究这篇文章的学习笔记。函数科里化的基本应用,也是函数式编程中运算管道构建的基本方法。四资料参考函数式编程指南 本文是Rxjs 响应式编程-第二章:序列的深入研究这篇文章的学习笔记。示例代码托管在:http://www.github.com/dashnowords/blogs 更多博文:《大史住在大前端》目录 showImg(https://segme...

    MorePainMoreGain 评论0 收藏0
  • 【CuteJavaScript】Angular6入门项目(3.编写服务和引入RxJS

    摘要:发布通过回调方法向发布事件。观察者一个回调函数的集合,它知道如何去监听由提供的值。 本文目录 一、项目起步 二、编写路由组件 三、编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四、编写服务 1.为什么需要服务 2.编写服务 五、引入RxJS 1.关于RxJS 2.引入RxJS 3.改造数据获取方式 六、改造组件 1.添...

    RebeccaZhong 评论0 收藏0
  • 从命令响应(一)

    摘要:响应式命令式这两种编程风格的思维方式是完全相反的。第二种方式是工人主动去找工人索取生产手机所要的零件,然后生产一台完整的手机,这两种方式就对应的响应式和命令式。 angular2中内置了rxjs,虽然框架本身并没有强制开发者使用响应式风格来组织代码,但是从框架开发团队的角度可以看出他们必然是认同这种编程风格的。rxjs本质是基于函数式编程的响应式风格的库,函数式相对于面向对象来说更加抽...

    JayChen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<