angular的学习进阶(十七)
虚拟滚动条
响应式应用
有趣的代码
https://1loc.dev/
rxjs 运算符相互的区别
export type Shape = '1' | '2';
export type Card = '3' | '4';
public shapes$ = new Subject<Shape>();
public cards$ = new Subject<Card>();
zip 每次压缩的可观察值,一组一组发
zip(of(1),of(2)).subscribe(console.log)
ngOnInit(): void {
zip(this.shapes$, this.cards$).subscribe(console.log);
this.shapes$.next('1');
this.cards$.next('3');
this.shapes$.next('2');
this.cards$.next('3');
this.shapes$.next('1');
this.cards$.next('4');
// 不需要complete 也能执行
}
["1", "3"]
["2", "3"]
["1", "4"]
forkJoin 仅在完成之后才发出最后一组的值
forkJoin(of(1),of(2)).subscribe(console.log)
[1,2]
ngOnInit(): void {
forkJoin(this.shapes$, this.cards$).subscribe(console.log);
this.shapes$.next('1');
this.cards$.next('3');
this.shapes$.next('2');
this.cards$.next('3');
this.shapes$.next('1');
this.cards$.next('4');
// 在complete完成后才会执行
this.shapes$.complete()
this.cards$.complete()
}
["1", "4"]
combineLatest 每次可观察对象发出新值,会发出之前的值
ngOnInit(): void {
combineLatest(this.shapes$, this.cards$).subscribe(console.log);
this.shapes$.next('1');
this.cards$.next('3');
// 2次
this.shapes$.next('2');
this.cards$.next('3');
// 2次
this.shapes$.next('1');
this.cards$.next('4');
//1 次
// 不需要complete
}
// ["1", "3"]
// ["2", "3"]
// ["2", "3"]
// ["1", "3"]
// ["1", "4"]
distinctUntilChanged 运算符
number,string
[1, 2, 3, 1, 3] of(1, 1, 2, 2, 3, 3, 1, 3, 3, 3).pipe(distinctUntilChanged(), toArray()).subscribe(console.log);
// [1, 2, 3, 1, 3]
of('a', 'A', 'b', 'c', 'D', 'd', 'e', 'f', 'G', 'g', 'h').pipe(
// 把前一个字和后一个值转化成小写,所以相同的就可以跳过
// c ,p 可以理解成前一个值,后一个值
distinctUntilChanged((c, p) => c.toLowerCase() === p.toLowerCase()),
toArray()
).subscribe(console.log);
// ["a", "b", "c", "D", "e", "f", "G", "h"]
对象流
将对对象引用的比较,而不是对象属性值得比较
from([obj,obj,obj]).pipe(distinctUntilChanged()).subscribe(console.log)
// {name: "xxx"}
但是我们写成函数进行判断
from([
{ name: 'Porsche', model: '911' },
{ name: 'Porsche', model: '911' },
{ name: 'Ferrari', model: 'F40' }
]).pipe(
distinctUntilChanged((p,n)=>{
return p.name===n.name&&p.model===n.model
}),
).subscribe(console.log)
// {name: "Porsche", model: "911"}
// {name: "Ferrari", model: "F40"}
两个参数的情况
from([
{ name: 'Porsche', model: '911' },
{ name: 'PORSCHE', model: '911' },
{ name: 'Ferrari', model: 'F40' },
{ name: 'FERRARI', model: 'F40' },
]).pipe(
distinctUntilChanged(
(p,n)=>{
return p.toLowerCase()===n.toLowerCase()
},/*第二个函数你可以理解成 c=>v.name
:{} 我差点蒙蔽了,其实就是做了typescript做的类型限制
*/
(car:{name:string,model:string})=>car.name
),
).subscribe(console.log)
// {name: "Porsche", model: "911"}
// {name: "Ferrari", model: "F40"}
封装input 延迟输入指令
ng g d delayed/delayed-input
import {Directive, OnInit,OnDestroy,ElementRef, EventEmitter, Input, Output} from '@angular/core';
import {from, fromEvent, Subject, timer} from 'rxjs';
import {debounce, distinctUntilChanged, takeUntil} from 'rxjs/operators';
@Directive({
selector: '[appDelayedInput]'
})
export class DelayedInputDirective implements OnInit,OnDestroy{
// 声明的 destroy$,配合 takeUntil 运算符一起使用,当指令销毁时,取消订阅
private destroy$ = new Subject<void>();
// debounce 配合timer 使用 500ms发出一个值
@Input() delayTime = 500;
@Output() delayedInput = new EventEmitter<Event>();
// 拿到DOM
constructor(private elementRef: ElementRef<HTMLInputElement>) {
}
ngOnInit() :void{
fromEvent(this.elementRef.nativeElement,'input').pipe(
debounce(()=>timer(this.delayTime)),
distinctUntilChanged(null,
(event:Event)=>(event.target as HTMLInputElement).value
),
takeUntil(this.destroy$)
).subscribe(e=>this.delayedInput.emit(e))
}
ngOnDestroy() {
this.destroy$.next();
}
}
使用
<input type="text" appDelayedInput [delayTime]="600" (delayedInput)="search($event)">
search(event: Event) {
console.log((event.target as HTMLInputElement).value);
}
rxjs publish() 冷热处理
let a = interval(1000).pipe(
tap(v => console.log('打印下'+v)),
take(5),
// 冻住
publish()
) as ConnectableObservable<number>
a.subscribe(res => {
console.log(res);
}, err => {
}, () => {
console.log('再见');
});
// 解除
a.connect();
// 打印下0
// 0
// 打印下1
// 1
// 打印下2
// 2
// 打印下3
// 3
// 打印下4
// 4
// 再见
rxjs delayWhen
of(10).pipe(delayWhen(_=>timer(1000))).subscribe(res=>{
console.log(res);
})
// 延迟传递值, 传递的参数是一个函数,这种方法好像比较实用
合并
let a=interval(1000).pipe(take(4))
let b=interval(1000).pipe(take(2))
zip
zip(a,b).subscribe(console.log)
// [ 0, 0]
// [ 1, 1]
最小分母数
merge
merge(a,b).subscribe(console.log)
// 0 两次
// 1 两次
// 1
// 2
// 3
concat
concat(a,b).subscribe(console.log)
// 0
// 1
// 2
// 3
// 0
// 1
rxjs 运算
// 观察每次状态的改变
of(1,2,3,4,5).pipe(scan((acc,val)=>acc+val,0)).subscribe(console.log)
// 1
// 3
// 6
// 10
// 15
// 计算点击的次数
fromEvent(this.ccc.nativeElement,'click').pipe(scan((acc,val)=>++acc,0)).subscribe(console.log)
of(1,2,3,4,5).pipe(max()).subscribe(console.log)
// 5
of(1,2,3,4,5).pipe(min()).subscribe(console.log)
// 1
of(1,2,3,4,5).pipe(reduce((acc,val)=>acc+val)).subscribe(console.log)
// 15
of({ name : 'chris' },{ age : 11 }).pipe(
reduce((acc,curr) => ({ ...acc, ...curr}))
).subscribe(console.log)
// {name: "chris", age: 11}
rxjs buffer缓冲
interval(200).pipe(
buffer(timer(1000))
).subscribe(console.log)
// [0,1,2,3]
//值发生一次
interval(1000).pipe(
buffer(interval(4000))
).subscribe(console.log)
// [0, 1, 2]
// [3, 4, 5, 6]
// [7, 8, 9, 10]
// bufferWhen 参数函数 可以用数组长度看相隔多少s点击一次
interval(1000).pipe(
bufferWhen(() => fromEvent(this.ccc.nativeElement, 'click'))
).subscribe(console.log);
buferCount
收集缓冲区并在指定数量的值后,作为数组发出
const src$ = merge(
of(1).pipe(delay(1000)),
of(2).pipe(delay(4000)),
of(3).pipe(delay(5000)),
);
src$.pipe(
bufferCount(3)
).subscribe(console.log)
=============
interval(1000).pipe(
bufferCount(3)
).subscribe(console.log)
// [0,1,2]
// [3,4,5]
// [6,7,8]
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者
总有一天我也能成为大佬