当在Angular2, Angular4, Angular5, Angular6中遇到异步处理导致页面更新不正确的情况时,可以采取以下对策措施
我在Angular中使用Pusher,在异步处理中遇到了屏幕未正确更新的问题,所以在此记录解决方法。
当我们想要在JS、手机等设备上实现与服务器的实时通信(即推送)时,Pusher是一个很方便易用的服务。
这次我们使用了 pusher-js。
总的来说,我认为使用NgZone最容易。
这篇文章的组织
-
- 异步处理等导致界面更新不执行的情况及其代码
-
- 发生原因
-
- 解决方法1(NgZone)
-
- 解决方法2(DetectChangeRef)
- 总结
1. 导致屏幕不更新的事件的发生方式和对应代码,例如异步处理。
我担心这次Angular可能无法正常工作,因为我们使用了外部库(前一次在Vue中实现时没有问题)。
这次我们使用了Pusher库,以下是代码示例。
const Pusher = require('pusher-js');
// TODO: 環境ごとに値が変わるようにする
export default new Pusher('xxxxxxxx', {
cluster: 'ap1',
encryped: true,
});
import { default as testPusher } from 'models/pusher.ts'
@Component({
selector: 'app-real-time',
template: `
<div *ngIf="error"> {{ error }} </div>
<div>Pusherテスト</div>
`
})
export class RealTimeComponent implements OnInit {
error: string;
ngOnInit(): void {
this.error = null;
const channel = testPusher.subscribe('xxxxxxxx');
channel.bind('error', (data) => {
this.error = 'エラーが発生しました';
})
}
}
看起来似乎没有问题,但是这样一来,错误的选项将无法显示在屏幕上。
造成的原因
Angular中有一个名为Zone的概念。
参考链接:
https://angular.io/guide/glossary#zone
我认为,当使用Pusher的channel.bind进行异步处理时,这是因为它在Zone的外部,导致Angular无法检测到更改。
方法1解决方案(NgZone)
只要在Zone的外面,把它放进去不就好了嘛,这就是个简单而强大的解决方法。
import { default as testPusher } from 'models/pusher.ts'
@Component({
selector: 'app-real-time',
template: `
<div *ngIf="error"> {{ error }} </div>
<div>Pusherテスト</div>
`
})
export class RealTimeComponent implements OnInit {
error: string;
constructor(private zone: NgZone) {}
ngOnInit(): void {
this.error = null;
const channel = testPusher.subscribe('xxxxxxxx');
channel.bind('error', (data) => {
this.zone.run(() => {
this.error = 'エラーが発生しました';
});
})
}
}
很简单,这样就解决了。
this.zone.run(() => {
// コード
});
通过将其放入Zone中,这就是将其放入Zone中的意思。
第二种解决方法(DetectChangeRef)
如果没有自动变更检测,我们可以手动触发检测。这是一种思路。
import { default as testPusher } from 'models/pusher.ts'
@Component({
selector: 'app-real-time',
template: `
<div *ngIf="error"> {{ error }} </div>
<div>Pusherテスト</div>
`
})
export class RealTimeComponent implements OnInit {
error: string;
constructor(private detectChange: DetectChangeRef) {}
ngOnInit(): void {
this.error = null;
const channel = testPusher.subscribe('xxxxxxxx');
channel.bind('error', (data) => {
this.error = 'エラーが発生しました';
this.detectChange.detectChanges();
})
}
}
在代码执行后,明确地调用了变更检测。
// コード
this.detectChange.detectChanges(); // <- 変更検知の発動
这个问题也可以很简单地解决。
概述
这两种解决方法都可以简单地解决,但我认为前者更好。因为在后者的情况下,比如想要调用 this.router.navigate() ,它无法正常运行。那是因为它已经进入了另一个页面,这是理所当然的。表现是,虽然会跳转到另一个页面,但 ngOnInit 等方法不会被调用。
如果使用NgZone的方法,就可以避免这种情况。这是因为进入了Zone内部。
最后
能否让框架本身不再关注这种事情呢?