当在Angular2, Angular4, Angular5, Angular6中遇到异步处理导致页面更新不正确的情况时,可以采取以下对策措施

我在Angular中使用Pusher,在异步处理中遇到了屏幕未正确更新的问题,所以在此记录解决方法。

当我们想要在JS、手机等设备上实现与服务器的实时通信(即推送)时,Pusher是一个很方便易用的服务。

这次我们使用了 pusher-js。

总的来说,我认为使用NgZone最容易。

这篇文章的组织

    1. 异步处理等导致界面更新不执行的情况及其代码

 

    1. 发生原因

 

    1. 解决方法1(NgZone)

 

    1. 解决方法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内部。

最后

能否让框架本身不再关注这种事情呢?

广告
将在 10 秒后关闭
bannerAds