node 10.15.0
ionic 4.12.0
cordova 9.0
# platforms
cordova-android:8.0.0
cordova-ios: 5.0.0
为什么要管理订阅?
问题一:Memory leak
场景:
页面操作顺序:TestPage -> Test2Page ->(返回到) TestPage
Test2Page中模拟接口请求,发射数据并订阅
Chrome Memory 实验结果如下:
in TestPage
in Test2Page
back TestPage
此时Test2Page仍在内存中。
解决:在OnDestory()取消订阅
export class Test2Page implements OnInit, OnDestroy {
subs: Subscription;
msgData = 'xxxxx2';
constructor() {
}
ngOnInit() {
this.subs = this.makeData()
.pipe(delay(5000))
.subscribe(
data => {
console.log('makeData2 -> data = ' + data[0].name);
this.msgData = data[0].name;
},
error2 => console.log('makeData2 -> error = ' + error2)
);
}
makeData(): Observable<any> {
return of([{id: 1, name: 'BBB'}]);
}
ngOnDestroy(): void {
this.subs.unsubscribe();
}
}
验证效果
问题二:Html render
场景和问题一一样,只是此时Test2Page页面数据源慢尚未返回,操作返回到TestPage,
回到TestPage后,接口数据到了,由于未取消订阅,所以会继续执行处理数据方法,然后渲染页面,但此时Test2Page页面已被销毁,无法渲染。
实现订阅管理
定义SubSink类
_subs: SubscriptionLike[] 存储订阅对象,便于取消订阅
const isFunction = (fn: any) => typeof fn === 'function';
export interface SubscriptionLike {
unsubscribe(): void;
}
export class SubSink {
protected _subs: SubscriptionLike[] = [];
constructor() {
}
add(...subscriptions: SubscriptionLike[]) {
this._subs = this._subs.concat(subscriptions);
}
set sink(subscription: SubscriptionLike) {
this._subs.push(subscription);
}
unsubscribe() {
this._subs.forEach(sub => sub && isFunction(sub.unsubscribe) && sub.unsubscribe());
this._subs = [];
}
}
定义BaseComponent类
BaseComponent实现OnDestroy方法,及组件销毁时取消订阅
export class BaseComponent implements OnDestroy {
subs = new SubSink();
/**
* Component销毁时会取消订阅
*/
ngOnDestroy(): void {
this.subs.unsubscribe();
}
}
使用
首先继承BaseComponent;
其次构造方法:super();
最后将订阅对象赋值给 this.subs.sink,此时会调用SubSink类中的
this._subs.push(subscription);
export class Test2Page extends BaseComponent implements OnInit {
msgData = 'xxxxx2';
constructor(private changeRef: ChangeDetectorRef) {
super();
}
ngOnInit() {
this.subs.sink = this.makeData()
.pipe(delay(5000))
.subscribe(
data => {
console.log('makeData2 -> data = ' + data[0].name);
this.msgData = data[0].name;
this.changeRef.detectChanges();
},
error2 => console.log('makeData2 -> error = ' + error2)
);
}
makeData(): Observable<any> {
return of([{id: 1, name: 'BBB'}]);
}
}
测试
参考文献
how-to-automatically-unsubscribe
总结
总的来说,取消订阅还是很有必要的;通过上面的方式,极大程度的方便了使用。
下一篇将介绍全局异常处理。