我们有一个活动版本的应用程序,它开始轮询服务器立即获得一些基本信息-当请求失败时,它会在一秒后重试,直到请求成功。
在新版本中,我们删除了这个轮询:即旧端点不再存在。
现在的问题是,PWA从不更新更新的版本:也就是说,当一个已经打开旧版本的用户打开该应用程序时,他们会看到不断尝试轮询服务器的旧版本(当然,这个请求失败了)。但是,灵活的服务工作人员从不更新新版本(即刷新不起作用,关闭浏览器选项卡,或者整个浏览器也不工作)。
注意事项:这个问题似乎与投票有关
唯一的解决办法是按下CTRL+F5来重新加载应用程序。
但这当然不是一个解决方案,因为普通用户不知道他们可以/应该这样做.
知道如何解决这个问题吗?
复制和细节
下面是一个用于重现问题的测试应用程序:https://github.com/tmtron/pwa-test
自述文件包含完整的详细信息。
初始状态:
我们以前已经启动了应用程序,浏览器有PWA的版本,它不断地轮询服务器并失败。现在我们关闭浏览器选项卡(显示应用程序)
NGSW状态
http://127.0.0.1:8080/ngsw/state
NGSW Debug Info:
Driver state: NORMAL ((nominal))
Latest manifest hash: b5a9f50d4efae604dbc6706040c71ecb2029c1cf
Last update check: never
=== Version b5a9f50d4efae604dbc6706040c71ecb2029c1cf ===
Clients: 4cad0ef1-179e-4629-b20f-602c4a4be1f9, 4d82d618-8fac-4e79-938c-605266702fa1, e0813c6a-3b76-4aeb-a14a-9043f0417b24, 9bf1be36-e911-4612-8158-21819eb222dc, 09337bd0-d060-46a1-b9e9-56257b879bdd, 0ce1327b-05bb-44e1-bba7-f3769cbad9b2, c9b796dd-b20c-417b-a504-7f065fd841db
=== Idle Task Queue ===
Last update tick: 1s996u
Last update run: never
Task queue:
* init post-load (update, cleanup)
* initialization(b5a9f50d4efae604dbc6706040c71ecb2029c1cf)
* check-updates-on-navigation
Debug log:启动计时器的应用程序组件:
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'pwa-update-test-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
private timerId?: number;
status = 'uninit';
version = 1;
timerActive = true;
constructor(private readonly httpClient: HttpClient) {}
ngOnInit() {
this.status = 'waiting';
const timerUrlParamIsPresent = window.location.href.indexOf('timer') >= 0;
this.timerActive = timerUrlParamIsPresent
if (this.timerActive) {
this.timerId = window.setTimeout(() => this.getAppInfo());
} else {
this.status = 'timer deactivated via URL parameter';
}
}
getAppInfo() {
this.status = 'working';
this.httpClient
.get(window.location.origin+'/non-existing-end-point')
.toPromise()
.catch((err) => {
this.status =
new Date().toLocaleTimeString() + ' failed! (will retry soon)';
console.log('getAppInfo failed', err);
if (this.timerActive) {
this.timerId = window.setTimeout(() => this.getAppInfo(), 2000);
}
});
}
stopTimer() {
this.timerActive = false;
clearTimeout(this.timerId);
}
}ngsw-config
{
"$schema": "../../node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/manifest.webmanifest",
"/*.css",
"/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}发布于 2020-12-19 18:43:53
发布同样的答案,我张贴了https://github.com/angular/angular/issues/40207#issuecomment-748509879的能见度。
首先,用于非常详细的复制,@tmtron❤️
它确实有助于找出问题所在。
所以,下面是发生的情况:
为了避免干扰应用程序请求,SW将不会立即检查初始化和导航请求的更新。Insted将安排一项任务,在“空闲”时检查更新情况。目前,SW被认为是空闲的,当它没有收到任何请求5秒。
因此,由于您的应用程序不断轮询(即发送请求),SW绝不会认为自己是空闲的,因此永远不会执行检查更新任务。
注意:这与请求失败或成功无关。当请求失败时,您的应用程序就会在5s空闲阈值之前发送一个新的。
尽管应用程序在失败时不断地发出相同的请求可能不是一个好主意(例如,应该有一些退避机制),但SW应该能够更优雅地处理这个问题:咧嘴笑:
例如,即使SW不被视为“空闲”,IdleScheduler也可能有最大延迟(比如30),在此之后,它执行其挂起的任务。
就工作而言,客户端可以做几件事来避免这个问题:
IdleScheduler,因此它不受问题的影响。当然,如果您不能更新客户端代码,这些都不会对您有所帮助(因为SW一直在为旧版本服务)。
恐怕,您只能从服务器上为轮询请求返回一个2xx响应(这样应用程序就可以停止轮询,SW可以自我更新)。
或者(或另外),您可以将服务器配置为包含删除端点的清场数据头(带有"storage"或"*"指令),以便提出该请求的老客户端的SW将被浏览器取消注册。
https://stackoverflow.com/questions/65340710
复制相似问题