[Angular 基础] - Observable

2024-03-09 07:20

本文主要是介绍[Angular 基础] - Observable,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[Angular 基础] - Observable

之前的笔记:

  • [Angular 基础] - service 服务
  • [Angular 基础] - routing 路由(上)
  • [Angular 基础] - routing 路由(下)

我以前对 Observable 的理解是 Promise 的一个超集,重新了解了一下,感觉这个说法不太对。更准确一些的说法应该是 stream of events,只不过大多数情况下,这个 events 是一个 Promise 而已。比如说 routes 的变化不属于 Promise,但是它是一个 event,也就自然可以被放到 Observable 里面去进行监听。

大多数 rxjs 相关的内容都在 rxjs 的 observable 里面说过了,这里主要记一些 angular 相关的知识,之后有需求的话继续拓展

先简单的说明一下项目结构:

❯ tree src/app/
src/app/
├── app-routing.module.ts
├── app.component.css
├── app.component.html
├── app.component.ts
├── app.module.ts
├── home
│   ├── home.component.css
│   ├── home.component.html
│   └── home.component.ts
└── user├── user.component.css├── user.component.html└── user.component.ts3 directories, 11 files

路由的配置如下:

const routes: Routes = [{ path: '', component: HomeComponent },{ path: 'user/:id', component: UserComponent },
];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule],
})
export class AppRoutingModule {}

简单来说,项目总共有两个路径,默认路径渲染 Home 组件,user/:id 路径渲染 User 组件

使用 observable

这里的变化会在 HomeComponent 中实现:

@Component({selector: 'app-home',templateUrl: './home.component.html',styleUrls: ['./home.component.css'],
})
export class HomeComponent implements OnInit {constructor() {}ngOnInit() {interval(1000).subscribe((count) => {console.log(count);});}
}

其中,interval 是 rxjs 提供的一个 observable:

export declare function interval(period?: number,scheduler?: SchedulerLike
): Observable<number>;

这个函数会在每个周期输出一个数字 (counter),效果如下:

在这里插入图片描述

需要注意这里有一个问题——当页面离开 Home 时,log 并没有停止。而是会持续输出。这个原因是因为这是一个 custom observable,它的创建和终结并不由 Angular 进行控制。因此如果不在 NgOnDestroy 中销毁,它就会一直 log 下去

最糟糕的情况是,如果当前组件被重复创建/毁灭多次,那么同一个组件就会有多个 observable 一起运行:

在这里插入图片描述

解决方法如下:

export class HomeComponent implements OnInit, OnDestroy {private intervalSub: Subscription;constructor() {}ngOnInit() {this.intervalSub = interval(1000).subscribe((count) => {console.log(count);});}ngOnDestroy(): void {this.intervalSub.unsubscribe();}
}

效果如下:

在这里插入图片描述

可以看到,当切换到其他页面的时候,log 的输出停止了,而从其他页面回来的时候,subscription 中的输出被清零了

👀:subscribe 中的就是 observer,它主要的目的就是用来提供 Observable 中,对下一步操作的处理,包括 next, errorcomplete

创建 observable

这里会创建一个新的 observable:

export class HomeComponent implements OnInit, OnDestroy {private intervalSub: Subscription;constructor() {}ngOnInit() {const customIntervalObservable = new Observable((observer) => {let count = 0;setInterval(() => {observer.next(++count);console.log(customIntervalObservable);}, 1000);});this.intervalSub = customIntervalObservable.subscribe((data) => {console.log(data);});}ngOnDestroy(): void {this.intervalSub.unsubscribe();}
}

实现效果如下:

在这里插入图片描述

可以看到,实现方面基本上没什么变化

error

报错也是一个常见的情况,observable 这里对于报错的处理也很简单,如下:

const customIntervalObservable = new Observable((observer) => {let count = 0;setInterval(() => {observer.next(++count);if (count > 3) {observer.error('count is greater than 3');}}, 1000);
});

效果如下:

在这里插入图片描述

可以看到,一旦报错后,observable 就会被取消。在 subscription 处理报错方法如下:

this.intervalSub = customIntervalObservable.subscribe((data) => {console.log(data);},(error) => {console.error(error);}
);

效果如下:

在这里插入图片描述

complete

因为 observable 是一个 stream of events,所以它也有提供对于 events 终止的处理,也即是 complete:

const customIntervalObservable = new Observable((observer) => {let count = 0;setInterval(() => {observer.next(++count);if (count === 2) {observer.complete();}if (count > 3) {observer.error('count is greater than 3');}}, 1000);
});this.intervalSub = customIntervalObservable.subscribe((data) => {console.log(data);},(error) => {console.error(error);},() => {console.log('%c' + 'Observable completes.', 'color: #198754; ');}
);

效果如下:

在这里插入图片描述

⚠️:如果在 complete 之前 subscription 就遇到报错,那么 complete 状态不会被触发

operators

先前的笔记有提到过,rxjs 说自己是 observable 版本的 lodash,其中一个原因就是 rxjs 提供了相当丰富的 operators,这样操作 observables 变得更加简单——虽然所有功能都是可以写在 observer 中进行实现的,不过 rxjs 提供的了很多开箱即用的 operators,可以极大地简化操作

pipe 的用处在于可以接受多个 operators,并合并成一个 operators。这样 operator 就可以承接上一个 observable 的返回值,对其进行修改,并 emit 到一个 新的 observable 中。这代表通过 pipe 对数据进行操作,并不会修改上一个 observable 中的值。

map

这里使用 map 做一个案例,修改的代码如下:

const customIntervalObservable = new Observable((observer) => {let count = 0;setInterval(() => {observer.next(++count);}, 1000);
});const pipedObservable = customIntervalObservable.pipe(map((data: number) => {console.log('%c' + 'Round: ' + (data + 1), 'color: #0d6efd; ');return data + 1;})
);customIntervalObservable.subscribe((data) => {console.log('%c' + data, 'color: #198754; ');
});pipedObservable.subscribe((data) => {console.log('%c' + data, 'color: #bada55; background-color: #222;');
});

输出结果为:

在这里插入图片描述

可以看到:

  • 原本的 observable ——即绿色字体的 observable——其 conter 从 1 开始输出
  • 通过 pipe,并使用 map 进行操作的 observable——即黄色字体的 observable——其 conter 从 2 开始输出

这也说明了通过 pipe 的 operator 对数据操作,并不会 修改上一个 observable 的值

filter

修改如下:

const customIntervalObservable = new Observable((observer) => {let count = 0;setInterval(() => {observer.next(++count);}, 1000);
});const pipedObservable = customIntervalObservable.pipe(filter((data: number) => {return data % 2 === 0;}),map((data: number) => {return 'Round: ' + (data + 1);})
);pipedObservable.subscribe((data) => {console.log('%c' + data,'color: #bada55; background-color: #222; padding: 5px;');
});

其中:

  • filter 过滤掉所有的偶数值
  • map 只是单纯返回会被 log 的字符串

最终运行结果为:

在这里插入图片描述

⚠️:这里运行的 observable 只有一个,就是被 subscribed 的 pipedObservable

subjects

当需要触发一些数据的变动,并将其传递到不同的组件时,之前的做法都是使用 EventEmitter 去实现,如下面这个 service 案例:

import { EventEmitter, Injectable } from '@angular/core';@Injectable({providedIn: 'root',
})
export class UserService {activatedEmitter = new EventEmitter<boolean>();
}

实际上在应用的时候,也可以用 subject 去取代 EventEmitter,用法如下:

export class UserService {activatedEmitter = new Subject<boolean>();
}

替换了 Subject 后,就不能使用 emit,而是需要使用 next 去触发事件:

export class UserComponent implements OnInit {id: number;constructor(private route: ActivatedRoute,private userService: UserService) {}ngOnInit() {this.route.params.subscribe((params: Params) => {this.id = +params.id;});}
}

其他 subscribe 的使用方法一致。

二者使用上的区别主要是是否与 @Output 搭配使用,本质上来说 EventEmitter 的 interface 是这样的:

/*** Use in components with the `@Output` directive to emit custom events* synchronously or asynchronously, and register handlers for those events* by subscribing to an instance.*/
export declare interface EventEmitter<T> extends Subject<T> {}

它的初衷就是为了搭配 @Output 使用的,因此它做了一些限制,比如说只有父组件可以 subscribe @Output

项目部分更新

这里是对 第一个 Angular 项目 部分的更新,主要就是清理了一些代码,将 EventEmitter 更换成了 Subject,其他方面没什么大的变动

除了清理部分的代码修改如下:

在这里插入图片描述

这篇关于[Angular 基础] - Observable的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/789938

相关文章

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

vite搭建vue3项目的搭建步骤

《vite搭建vue3项目的搭建步骤》本文主要介绍了vite搭建vue3项目的搭建步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1.确保Nodejs环境2.使用vite-cli工具3.进入项目安装依赖1.确保Nodejs环境

Nginx搭建前端本地预览环境的完整步骤教学

《Nginx搭建前端本地预览环境的完整步骤教学》这篇文章主要为大家详细介绍了Nginx搭建前端本地预览环境的完整步骤教学,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录项目目录结构核心配置文件:nginx.conf脚本化操作:nginx.shnpm 脚本集成总结:对前端的意义很多

前端缓存策略的自解方案全解析

《前端缓存策略的自解方案全解析》缓存从来都是前端的一个痛点,很多前端搞不清楚缓存到底是何物,:本文主要介绍前端缓存的自解方案,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、为什么“清缓存”成了技术圈的梗二、先给缓存“把个脉”:浏览器到底缓存了谁?三、设计思路:把“发版”做成“自愈”四、代码

通过React实现页面的无限滚动效果

《通过React实现页面的无限滚动效果》今天我们来聊聊无限滚动这个现代Web开发中不可或缺的技术,无论你是刷微博、逛知乎还是看脚本,无限滚动都已经渗透到我们日常的浏览体验中,那么,如何优雅地实现它呢?... 目录1. 早期的解决方案2. 交叉观察者:IntersectionObserver2.1 Inter

Vue3视频播放组件 vue3-video-play使用方式

《Vue3视频播放组件vue3-video-play使用方式》vue3-video-play是Vue3的视频播放组件,基于原生video标签开发,支持MP4和HLS流,提供全局/局部引入方式,可监听... 目录一、安装二、全局引入三、局部引入四、基本使用五、事件监听六、播放 HLS 流七、更多功能总结在 v

Spring的基础事务注解@Transactional作用解读

《Spring的基础事务注解@Transactional作用解读》文章介绍了Spring框架中的事务管理,核心注解@Transactional用于声明事务,支持传播机制、隔离级别等配置,结合@Tran... 目录一、事务管理基础1.1 Spring事务的核心注解1.2 注解属性详解1.3 实现原理二、事务事

JS纯前端实现浏览器语音播报、朗读功能的完整代码

《JS纯前端实现浏览器语音播报、朗读功能的完整代码》在现代互联网的发展中,语音技术正逐渐成为改变用户体验的重要一环,下面:本文主要介绍JS纯前端实现浏览器语音播报、朗读功能的相关资料,文中通过代码... 目录一、朗读单条文本:① 语音自选参数,按钮控制语音:② 效果图:二、朗读多条文本:① 语音有默认值:②

vue监听属性watch的用法及使用场景详解

《vue监听属性watch的用法及使用场景详解》watch是vue中常用的监听器,它主要用于侦听数据的变化,在数据发生变化的时候执行一些操作,:本文主要介绍vue监听属性watch的用法及使用场景... 目录1. 监听属性 watch2. 常规用法3. 监听对象和route变化4. 使用场景附Watch 的

前端导出Excel文件出现乱码或文件损坏问题的解决办法

《前端导出Excel文件出现乱码或文件损坏问题的解决办法》在现代网页应用程序中,前端有时需要与后端进行数据交互,包括下载文件,:本文主要介绍前端导出Excel文件出现乱码或文件损坏问题的解决办法,... 目录1. 检查后端返回的数据格式2. 前端正确处理二进制数据方案 1:直接下载(推荐)方案 2:手动构造