Angular中防抖的实现

在angular中的防抖主要是利用了rxjs中的debounce操作符,非常简洁方便!

下面主要是介绍其三种写法,主要分为两类,不封装指令的防抖和封装成指令的防抖,其中封装成指令的防抖又有两种写法:利用@HostListener装饰器和Renderer2.listen方法。

不封装指令的防抖

html

1
2
3
4
5
6
7
8
9
10
11
12
<div class="syn-margin-24">
<p>
默认时间500ms,只有500ms以内没有事件触发才会执行。
</p>
<div class="syn-margin-24 label-font">
{{total}}
</div>
<button nz-button nzType="primary" (click)="add($event)">
<i nz-icon nzType="plus" nzTheme="outline"></i>
Add
</button>
</div>

ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { debounceTime } from 'rxjs/operators';
import { Component,OnDestroy} from '@angular/core';
import { Subscription, Subject } from 'rxjs';

@Component({
selector: 'syn-debounce',
templateUrl: './debounce.component.html',
styleUrls: ['./debounce.component.scss']
})
export class DebounceComponent implements OnDestroy{
public total: number = 0;
public debounceTime: number = 500;
public subscription = new Subscription();
public subject$ = new Subject();
constructor() {
this.subscription = this.subject$.pipe(debounceTime(this.debounceTime)).subscribe(e => this.total+=1);
}
add($event: MouseEvent) {
this.subject$.next($event);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}

效果图

定义了一个“主体”Subject,它既是观察者,也可以是订阅者,每次点击事件时调用add方法,然后使用next方法发送事件,然后由于subject$同时subscribe(订阅)了事件,所以会经过rxjs的debounce运算符的处理,其接收一个number类型的值,单位ms,就是我们设置的防抖时间。

指令-@HostListener

在实际的项目中用到防抖的地方肯定不只有一处,所以此时我们应该封装出一个防抖的指令使用,在ng中监听dom事件的方法我所了解的有两种:@HostListener监听和Renderer2.listen方法。思路都是一样的,都是利用了rxjs,只不过就是监听元素事件的写法不同而已。

ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { Directive, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
/**
* @description 基于rxjs的debounceTime操作符的防抖
*/
@Directive({
selector: '[synDebounceClick]'
})
export class DebounceClickDirective implements OnInit, OnDestroy {
@Input() debounceTime = 500;
@Output('synDebounceClick') debounceClick = new EventEmitter();
private subject$ = new Subject();
private subscription!: Subscription;
constructor() { }
ngOnInit(): void {
this.subscription = this.subject$.pipe(
debounceTime(this.debounceTime)
).subscribe(e => this.debounceClick.emit(e))
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
@HostListener('click',['$event'])
clickEvent(event: any) {
event.preventDefault();
event.stopPropagation();
this.subject$.next(event);
}
}

导入相应的module后然后在HTML模板中使用即可

1
<button nz-button nzType="primary" (synDebounceClick)="add()">Primary Button</button>

指令-Renderer2.listen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { debounceTime } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
import { Directive, ElementRef, Renderer2, OnInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core';

@Directive({
selector: '[synDebounceRendererClick]'
})
export class DebounceRendererDirective implements OnInit, OnDestroy {
private subject$ = new Subject();
private subscription!: Subscription;
@Input() debounceTime: number = 500;
@Output('synDebounceRendererClick') synDebounceRendererClick = new EventEmitter();
constructor(private _elementRef: ElementRef, private _renderer2: Renderer2) {
this.subscription = this.subject$.pipe(debounceTime(this.debounceTime))
.subscribe(e => this.synDebounceRendererClick.emit());
}

ngOnInit(): void {
this._renderer2.listen(this._elementRef.nativeElement,'click',(e) => {
e.stopPropagation();
e.preventDefault();
this.subject$.next(e);
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}

}

html

1
<button nz-button nzType="primary" (synDebounceRendererClick)="add()">Primary Button</button>

以上就是我所知道的三种写法,至于@HostListener和Renderer2.listen()有什么区别我也不是很清楚多方查阅也没有得到具体的结果,我所了解的是@HostListener只能监听宿主元素,而Renderer2.listen()可以监听任意元素,如果还有其它不同请在评论区留言交流!

笔者水平有限,若有错误敬请指正,不明白的地方欢迎评论区留言交流~