Angular PWA 更新太慢

9gt*_*3wS 7 progressive-web-apps angular aws-amplify

我在生产中有一个 Angular 8 PWA 应用程序。我定期进行大量更新,因此我需要找到一种方法让用户在打开应用程序时获取这些更新。

如果没有特殊操作,应用程序将不会更新。如果您在浏览器上打开该应用程序,它会显示您上次打开该应用程序时的版本,即使我已将新捆绑包推送到生产环境。这似乎是 PWA 功能的不幸结果。

我使用 AWS Amplify 进行托管。

为了解决这个问题(我在这里问过一个问题),我尝试使用 swUpdate。

问题是它的工作速度太慢了。如果有更新,它会重新加载浏览器——但这需要一段时间。用户打开应用程序后通常需要几秒钟才能重新加载。所以你打开应用程序,4 到 6 秒后应用程序重新加载新版本。

有没有办法让 swUpdate 运行得更快?或者加载新应用程序版本的替代方法?

这是我的代码,我认为这是一个简单的实现:

app.component.ts:

import { Component } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

   constructor(private swUpdate: SwUpdate) {
   title = 'Great App'

   swUpdate.available.subscribe(event => {
        swUpdate.activateUpdate().then(() => {
            window.location.reload();
            console.log('there is an Update! Reloading now.')
      });
    })

   if (!this.swUpdate.isEnabled) {
      console.log('Not going to update');
    }
 }
Run Code Online (Sandbox Code Playgroud)

但这效果不佳,因为重新加载通常在用户访问应用程序后几秒钟发生(即使在良好的互联网连接下)。

我知道我还可以向人们显示一条消息,说“想要刷新新版本?” 但这并不能解决 swUpdate 当前工作缓慢的根本问题。

Nen*_*vic 4

问题

Angular 中的 Service Worker 有 4 种不同的注册策略,这决定了它何时向浏览器注册。

  1. registerWhenStable:<timeout>:应用程序稳定后立即注册(无待处理的微/宏任务),但不晚于毫秒。如果应用程序在几毫秒后仍未稳定(例如,由于重复的异步任务),ServiceWorker 无论如何都会被注册。如果省略,则只有在应用程序稳定后才会注册 ServiceWorker。
  2. registerImmediately: 立即注册。
  3. registerWithDelay:<timeout>:延迟毫秒注册。例如,使用registerWithDelay:5000在5秒后注册ServiceWorker。如果省略,则默认为 0,一旦所有挂起的微任务完成,它将尽快注册 ServiceWorker,但仍然是异步的。
  4. An Observable factory function:返回 Observable 的函数。该函数将在运行时用于获取并订阅 Observable,并且 ServiceWorker 将在第一个值发出后立即注册。

注意:AngularregisterWhenStable:30000默认使用. 这意味着它将首先等待应用程序稳定下来,然后注册一个 Service Worker(或者如果应用程序在此之前尚未稳定,它将在 30 秒后注册 Service Worker)。

解决方案

registerWhenStable:30000您可以设置registerImmediately策略而不是默认值。然后您可以添加APP_INITIALIZER并在其中检查是否有新版本并加载它。在这种情况下,如果有新版本可用,用户将看不到旧版本。

应用程序模块.ts

import { APP_INITIALIZER } from '@angular/core';
import { ServiceWorkerModule, SwUpdate } from '@angular/service-worker';
...
function initializeApp(): Promise<any> {
  return new Promise(async (resolve, reject) => {
    try {
      // Check if Service Worker is supported by the Browser
      if (this.swUpdate.isEnabled) {
        const isNewVersion = await this.swUpdate.checkForUpdate();
        // Check if the new version is available
        if (isNewVersion) {
          const isNewVersionActivated = await this.swUpdate.activateUpdate();
          // Check if the new version is activated and reload the app if it is
          if (isNewVersionActivated) window.location.reload();
          resolve(true);
        }
        resolve(true);
      }
      resolve(true);
    } catch (error) {
      window.location.reload();
    }
  });
}

...

@NgModule({
  ...
  imports: [
    ...,
    ServiceWorkerModule.register('ngsw-worker.js', {
      enabled: environment.production,
      registrationStrategy: 'registerImmediately',
    }),
  ],
  providers: [
    ...,
    { provide: APP_INITIALIZER, useFactory: initializeApp, deps: [SwUpdate], multi: true },
  ],
})
export class AppModule {}
Run Code Online (Sandbox Code Playgroud)