每个新的角度生产PWA版本上的“意外令牌<”,直到站点刷新

Smo*_*son 13 azure azure-web-sites typescript progressive-web-apps angular

我知道有一些类似的问题,例如在angular 2生产构建之后出现了如此意外的令牌,但实际上并没有回答我的问题。

基本上,我有一个角度应用程序及其PWA,现在每次我第一次访问该网站时,每次进行新的产品构建时,都会遇到类似的错误

在此处输入图片说明

然后刷新页面后,该站点即可正常运行。

在任何浏览器/设备上都会发生这种情况

现在我的怀疑是,每次更新应用程序时,捆绑的主要js文件都会更改,并且PWA已缓存了旧的js文件,而该应用程序仍在尝试使用新的js文件。

我的项目结构如下

我有一个根模块,可以延迟加载其他模块,而第一个加载的模块是我的帐户模块,因此用户可以登录

这是我的root-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';

const routes: Routes = [
    { path: '', redirectTo: '/account/login', pathMatch: 'full' },
    {
        path: 'account',
        loadChildren: 'account/account.module#AccountModule',
        data: { preload: true }
    }
];

@NgModule({
    imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })],
    exports: [RouterModule],
    providers: []
})
export class RootRoutingModule {
Run Code Online (Sandbox Code Playgroud)

}

因此从技术上讲,用户进入的第一个模块是帐户模块,但是显然必须从根模块重定向它。

所以我所做的是在我的根组件模块中,检查服务工作者是否需要像这样进行更新...

root.component.ts

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

export class...

constructor(
    private _sw: SwUpdate
) {
    if (this._sw.isEnabled) {
        this._sw.available
            .subscribe(() => {
                this._sw.activateUpdate()
                    .then(() => {
                        window.location.reload(true);
                    });
            });
    }
}     
Run Code Online (Sandbox Code Playgroud)

现在,应该检查是否有更新,然后刷新页面..但是它不起作用。

这是我的ngsw-config.json

{
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "App",
      "installMode": "prefetch",
      "resources": {
        "files": [
          "/favicon.ico",
          "/index.html",
          "/*.css",
          "/*.js",
          "!/main*.js"
        ]
      }
    }, {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/assets/**",
          "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
        ]
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我正在排除main.js应该消除此问题的块文件... 但是它没有

现在,当我在新建之后检查“网络”选项卡时,这就是我得到的

在此处输入图片说明 该图像是在我排除main.js文件之前

这是我从main.js电话中排除电话后的样子ngsw.config

在此处输入图片说明

然后,我想像这样使用哨兵错误处理程序尝试捕获此错误。

export class RavenErrorHandler implements ErrorHandler {
  handleError(err: any): void {
    console.log('error', err);
    if (err.toLowerCase().includes('token <')) {
        window.location.reload(true);
    } else {
        Raven.captureException(err);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但这不起作用,我在哨兵中遇到错误,但应用程序未重新加载?

现在,我已经进行了一些测试,如果您在私有模式下全新构建后访问该网站,您将永远不会收到错误,只有在您之前访问该网站时,它才会发生,这使我认为这是一个缓存问题

我的应用程序托管在Azure上并且我的应用程序使用Angular 7.27

任何帮助,将不胜感激!

J. *_*pas 11

该问题是由install引起的ServiceWorker,后者是一种干扰并缓存所有响应(包括刚刚更新的响应)的响应。

有关服务人员的更多信息:MDN-使用服务人员

https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers

这就是为什么window.location.reload()不起作用的原因,因为它确实发出了新请求,但是此请求已被安装ServiceWorker的服务器劫持,而不是让真实请求进入真实服务器并获取真实响应(更新的main.js和资产),而是只是将一个缓存的响应返回给您的应用,该响应是旧的main.js而不是更新的响应,这就是失败的原因。

更具体地说,ServiceWorker是缓存main.js和其他资产,它们在每次更改代码时都会生成。依次加载Main.js时,它会延迟加载(发出http请求)应用程序的其他块(例如5.xxxxx.js)。

当您对代码进行更改时,即使所有块(包括main.js和)都得到了更新,5.xxxxxx.js浏览器仍在运行旧版本main.js。这个较main.js旧的5.xxxxxx.js对象已不再存在。在这一点上,如果您以编程方式执行reload(),则安装程序将ServiceWorker使用较早的缓存main.js进行响应,该缓存随后又尝试5.xxxxx.js从不存在的服务器中延迟加载较旧的版本,因此会收到404 html错误的响应。因此,'<'令牌异常(实际上是404错误页面标签的第一个字符)。

网络面板截图中可以明显看出这一点。如果查看屏幕截图,您将看到main.js和其他所有资产都是从交付的(from service worker),这意味着这些是缓存的较早版本,并且与Web服务器中现在存在的实际更新文件不同。

要解决此问题,ServiceWorker必须将缓存配置为不缓存main.js,而始终让此请求传递给真实服务器,以获取该缓存的任何更新版本main.js,从而将延迟加载5.xxxx.js现在已实际存在于服务器中的更新块。网络服务器。

这里是你如何配置ngsw-config.jsonServiceWorker具体不缓存main.js文件(假设它是模式的main.xxxxxxx.js),以解决此问题:

{
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "App",
      "installMode": "prefetch",
      "resources": {
        "files": [
          "/favicon.ico",
          "/index.html",
          "/*.css",
          "/*.js",
          "/!main*.js"
        ]
      }
    }, {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/assets/**",
          "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
        ]
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

运行ng build --prod以重建您的应用程序并进行测试。

更新:

为确保这不是浏览器缓存问题,true请将第一个参数传递给window.location.reload()方法。这指示浏览器在不缓存资产的情况下执行重新加载:

root.component.ts

                    .then(() => {
                        window.location.reload(true);
                    });

Run Code Online (Sandbox Code Playgroud)

更新2:

根据Angular的文档,缓存已安装在客户端浏览器上的Service Worker。如果您更新Service Worker代码(如最近所做的那样),则必须在已经安装了Service Worker代码的客户端的所有浏览器中更新新的Service Worker代码。Angular文档指出有一个定期检查,但未指定它,它将检查Service Worker本身是否需要更新并执行更新。 https://angular.io/guide/service-worker-devops#service-worker-updates

也许,仅在生产中而不是在开发中或使用专用浏览器模式(未安装SW的干净浏览器面板)来解决该问题,似乎是这种情况。

为了验证这一点,您应该将使用Chrome开发人员工具在页面上运行的现有浏览器软件与应安装的更新软件代码进行比较。如果它们不同,则意味着它尚未更新,这就是导致问题的原因。

您可以使用出现此问题的Chrome浏览器执行此操作,然后转到Chrome开发者工具->应用程序标签->服务工作者(在左面板顶部),然后在浏览器中注册SW。单击“源”链接,它将打开软件的实际代码。逐行比较代码(或使用diff实用程序)与应该运行的更新的SW代码。


Rai*_*ins 6

仔细检查您的缓存头。Chrome浏览器并不总是监听无缓存。如果您最近访问过该网站,则chrome将从缓存中加载index.html。导致错误。

我们已经将index.html更改为不存储,这似乎可以解决问题。 https://gertjans.home.xs4all.nl/javascript/cache-control.html