为了账号安全,请及时绑定邮箱和手机立即绑定

Angular PWA 更新太慢

Angular PWA 更新太慢

翻过高山走不出你 2022-06-16 10:09:10
我有一个 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');    } }但这并不能很好地工作,因为重新加载通常会在用户进入应用程序后几秒钟发生(即使在良好的互联网连接情况下)。我知道我也可以向人们显示一条消息,说“想用新版本刷新吗?” 但这并不能解决当前 swUpdate 工作速度有多慢的根本问题。
查看完整描述

3 回答

?
回首忆惘然

TA贡献1847条经验 获得超11个赞

对于仍然遇到此问题的任何人,这是我为减少延迟所做的事情:

  • 在 APP_INITIALIZER 而不是 AppComponent 中检查更新,以便更早完成检查

  • 使用checkForUpdate方法

在你app.module.ts添加一个初始化函数:

{ provide: APP_INITIALIZER, useFactory: checkForUpdates, multi: true, deps: [SwUpdate /* Add whatever dependency is required here */] },

该函数应如下所示:

export const checkForUpdates = (swUpdate: SwUpdate): (() => Promise<any>) => {

  return (): Promise<void> =>

    new Promise((resolve) => {

      swUpdate.checkForUpdate();

      

      swUpdate.available.subscribe(() => {

          window.location.reload();

      });


      resolve();

    });

};

我不确定是否需要 APP_INITIALIZER 和 checkForUpdate,但这样我将延迟减少到 1-2 秒(而不是 5-10 秒甚至更多)


查看完整回答
反对 回复 2022-06-16
?
白猪掌柜的

TA贡献1893条经验 获得超10个赞

我使用 Carrm 和 NeNad 的两个答案在我的 Angular 应用程序中创建一个页面来管理更新。


我还使用@saithodev/ts-appversion包来管理版本。


稍后,我创建了一个脚本来更新每个构建的应用程序版本并将其显示在该页面上 (appversionupdate.mjs)。


然后,在 package.json 中,我将脚本设置为与 npm run 一起使用。


这样,当我构建和部署项目时,版本会根据需要进行更新。


我知道 OP 并没有要求所有这些,但它可能对社区有用。


menu.component.html


<mat-toolbar color="accent" fxLayout="row" fxLayoutAlign="space-between center" class="top-bar">

    <div fxLayout="row" fxLayoutAlign="start center">

        <h1>Atualização do app</h1>

    </div>


</mat-toolbar>


<main>

    <div>

        <h2>Versão do app instalada: {{versao}}</h2>

    </div>


    <h3>Status da atualização</h3>

    <!-- lista sem numeração em HTML -->

    <ul>

        <li *ngFor="let mensagem of mensagens">

            {{mensagem}}

        </li>

    </ul>


    <button mat-raised-button color="accent" (click)="atualizarApp();" class="botao">Atualizar app</button>

</main>

menu.component.ts


import { Component, OnInit } from '@angular/core';

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


import { versions } from 'src/_versions';


@Component({

  selector: 'app-menu',

  templateUrl: './menu.component.html',

  styleUrls: ['./menu.component.scss']

})

export class MenuComponent implements OnInit {


  mensagens: string[] = [];

  versao: string = 'Versão ainda não carregada pelo sistema.';


  constructor(

    private readonly swUpdate: SwUpdate,

    ) { }


  ngOnInit(): void {

    this.atualizarApp();

    this.versao = versions.version;

  }


  async atualizarApp() {

    this.mensagens?.push(`${this.getFormattedDate()} Iniciando atualização do app.`);

    try {

      // Check if Service Worker is supported by the Browser

      if (this.swUpdate.isEnabled) {

        this.mensagens?.push(`${this.getFormattedDate()} Verificado que este navegador suporta a atualização do app.`);

        const isNewVersion = await this.swUpdate.checkForUpdate();

        this.mensagens?.push(`${this.getFormattedDate()} Verificando se há nova versão a ser baixada.`);

        // Check if the new version is available

        if (isNewVersion) {

          this.mensagens?.push(`${this.getFormattedDate()} Nova versão do app encontrada. Fazendo download.`);

          const isNewVersionActivated = await this.swUpdate.activateUpdate();

          // Check if the new version is activated and reload the app if it is

          if (isNewVersionActivated) {

            this.mensagens?.push(`${this.getFormattedDate()} Nova versão baixada e instalada.`);

            window.location.reload();

          }

        } else {

          this.mensagens?.push(`${this.getFormattedDate()} O Materiale app já está atualizado.`);

        }

      } else {

        this.mensagens?.push(`${this.getFormattedDate()} Verificado que este navegador não suporta a atualização do app automática e outras funcionalidades.`);

        this.mensagens?.push(`${this.getFormattedDate()} Por favor, atualize o navegador para a versão mais recente. Baixe preferencialmente o Google Chrome.`);

      }


    } catch (error) {

      this.mensagens?.push(`${this.getFormattedDate()} Houve algum error ao tentar atualizar o app. Mensagem de erro: ${error}`);

      this.mensagens?.push(`${this.getFormattedDate()} É possível que este navegador não seja compatível. Por favor, atualize o navegador para a versão mais recente. Baixe preferencialmente o Google Chrome.`);

      this.mensagens?.push(`${this.getFormattedDate()} Mensagem de erro: ${error}`);

      //window.location.reload();

    }


  }


  getFormattedDate(): string {

    var date = new Date();

    var str = `${date.getDate()}/${(date.getMonth() + 1)}/${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;

    return str;

}


}

appversionupdate.mjs


import fs from 'fs';


//console.log("Atualizado versao do app");


var obj = JSON.parse(fs.readFileSync('package.json', 'utf8'));


var version = obj.version;

var major = version.split('.')[0];

var minor = version.split('.')[1];

var patch = version.split('.')[2];


var minor_updated = Number(minor) +1;

var version_updated = major + '.' + minor_updated + '.' + patch;


console.log("Nova versao: " + version_updated);


obj.version = version_updated;


let data = JSON.stringify(obj, null, 2);


fs.writeFileSync('package.json', data);

包.json


{

  "name": "testeapp",

  "version": "0.14.0",

  "scripts": {

    "ng": "ng",

    "prestart": "ts-appversion",

    "start": "ng serve",

    "prebuild": "ts-appversion",

    "build": "ng build",

    "watch": "ng build --watch --configuration development",

    "test": "ng test",

    "bd": "node ./appversionupdate.mjs && npm run build && firebase deploy"

  },


查看完整回答
反对 回复 2022-06-16
?
繁华开满天机

TA贡献1816条经验 获得超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。

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

解决方案

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

app.module.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 {}


查看完整回答
反对 回复 2022-06-16
  • 3 回答
  • 0 关注
  • 151 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号