Angular未捕获(在Promise中):ChunkLoadError:加载块XXX失败。

10
我正在使用Angular 12,并使用Service Worker部署新版本。似乎每次我将新版本部署到生产环境(但不知何故在暂存环境中不会出现此问题),一些用户会收到大量错误,如下所示:
Uncaught (in promise): ChunkLoadError: 加载块XXX失败。
虽然有很多关于这个问题的帖子,但我认为解决方案是因情况而异的。
与暂存环境相比,我的生产环境只有以下区别:
我调用 enableProdMode();
我的Service Worker设置如下:
    ServiceWorkerModule.register('ngsw-worker.js', {
      enabled:
        environment.env === Environment.PRODUCTION || environment.env === Environment.STAGING,
      registrationStrategy: 'registerWhenStable:30000',
    }),

我的配置看起来像这样

{
  "$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": "lazy",
      "resources": {
        "files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"]
      }
    }
  ]
}

这是我展示更新的方式(基本上是一个可以忽略的横幅):

@Component({
  selector: 'app-live-update-notifier',
  templateUrl: './live-update-notifier.component.html',
  styleUrls: ['./live-update-notifier.component.scss'],
})
export class LiveUpdateNotifierComponent implements OnDestroy {
  hasUpdate = false;
  isIgnored = false;

  private subscription = new SubSink();

  constructor(
    @Inject(DOCUMENT) private document: Document,
    private swUpdate: SwUpdate,
    private appRef: ApplicationRef,
    private changeDetectorRef: ChangeDetectorRef
  ) {
    const appIsStable$ = this.appRef.isStable.pipe(first((isStable) => isStable === true));
    const everySixHours$ = interval(6 * 60 * 60 * 1000);
    const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$);

    if (swUpdate.isEnabled) {
      this.subscription.add(
        everySixHoursOnceAppIsStable$.subscribe(() => swUpdate.checkForUpdate()),
        this.swUpdate.available.subscribe((evt) => {
          if (evt.type === 'UPDATE_AVAILABLE') {
            this.hasUpdate = true;
            this.changeDetectorRef.detectChanges();
          }
        })
      );
    }
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }

  updateApp() {
    this.document.location.reload();
  }

  skipNotification() {
    this.isIgnored = true;
  }
}

如果您有任何能够引导解决方案的线索,谢谢。

编辑:

我的应用程序中有许多延迟加载的模块。我认为问题的原因是某些模块在新版本之前没有加载,并且当用户在部署后导航到它们时,它们会出现这个“chunk”问题。但是我尝试通过以下方式来模拟此问题:

  • 编辑模块A和B
  • 部署
  • 导航到模块A
  • 编辑模块A和B
  • 部署
  • 从旧A导航到旧B,因此没有更新APP的版本 RES:旧B仍然正常加载

因此我不认为这是问题所在。


你能增加更详细的错误信息吗? - Apoorva Chikara
@ApoorvaChikara https://sentry.io/share/issue/32f52037029f43d8b355456ffbfe56de/ - Bobby
请提供另一个链接或访问 https://sentry.io/share/issue/87e6a59fdc4845f3b31b2abf91aebff4/。 - Bobby
我知道这是由于哈希文件不匹配导致的,这些文件在新部署时会得到更新,但我不明白为什么会发生这种情况,因为它应该只是 "installMode": "prefetch", 而不是硬删除缓存文件。 - Bobby
2个回答

8
这可能是由不同的原因引起的。但是,几个月前我使用了spock123提供的解决方法。
他说:
我们当然使用Service Worker服务swUpdate来获取新版本可用时的通知。然而,当有新的SW版本时,似乎有时并未更新所有惰性加载的块。
基本上,解决方法是在遇到导致错误ChunkLoadErrorbundle not found错误时重新加载应用程序。
你需要全局监听错误,如果应用程序遇到此错误,只需重新加载页面。 设置全局错误处理程序的说明

更新:

我引用自 angular.io

处理不可恢复的状态

在某些情况下,服务工作者用于为客户端提供服务的应用程序版本可能处于无法恢复的损坏状态,需要进行完整页面重新加载。

例如,想象以下场景:

一个用户第一次打开应用程序,服务工作者缓存了应用程序的最新版本。假设应用程序的缓存资产包括index.htmlmain.<main-hash-1>.jslazy-chunk.<lazy-hash-1>.js
用户关闭应用程序并有一段时间不打开它。
一段时间后,新版本的应用程序部署到服务器上。这个更新版本包括文件index.htmlmain.<main-hash-2>.jslazy-chunk.<lazy-hash-2>.js(请注意,哈希现在是不同的,因为文件的内容已经更改)。旧版本在服务器上不再可用。
与此同时,用户的浏览器决定从其缓存中清除lazy-chunk.<lazy-hash-1>.js。浏览器可能会决定从缓存中驱逐特定(或所有)资源以便回收磁盘空间。
用户再次打开应用程序。服务工作者在这一点上提供了它所知道的最新版本,即旧版本(index.htmlmain.<main-hash-1>.js)。
在稍后的某个时刻,应用程序请求懒惰的捆绑包lazy-chunk.<lazy-hash-1>.js
服务工作者无法在缓存中找到该资产(请记住,浏览器将其删除)。它也无法从服务器检索它(因为服务器现在只有来自新版本的lazy-chunk.<lazy-hash-2>.js)。
在上述情况下,服务工作者无法提供通常会被缓存的资产。那个特定的应用程序版本已经损坏了,没有办法修复客户端的状态而不重新加载页面。在这种情况下,服务工作者通过发送UnrecoverableStateEvent事件来通知客户端。订阅SwUpdate#unrecoverable以获得通知并处理这些错误。

handle-unrecoverable-state.service.ts:

@Injectable()
export class HandleUnrecoverableStateService {
  constructor(updates: SwUpdate) {
    updates.unrecoverable.subscribe(event => {
      notifyUser(
        'An error occurred that we cannot recover from:\n' +
        event.reason +
        '\n\nPlease reload the page.'
      );
    });
  }
}

谢谢,是的,我也找到了那个解决方案,但感觉更像是一种权宜之计而不是一个正确的解决方案。目前我正在生产环境中这样做,以避免出现此类问题。但了解为什么所有块没有从 SW 正确加载是我的主要关注点。这是 Angular SW 直接的问题吗? - Bobby
我不能确定,但我认为是这样。 - Mahdi Zarei
这可能是由很多原因引起的,包括Angular本身、浏览器版本、构建配置等等。 - Mahdi Zarei
好的,我猜在我的情况下没有简单的方法知道问题所在^^ - Bobby
哦,好的,谢谢你告诉我。我可能跳过了文档底部的那一部分哈哈。我会再等一会儿,尝试这个解决方案并指定悬赏/解决方案。感谢你的时间! - Bobby

0

nginx服务器的配置:

location ~* \.(?:css|js)$ {
    add_header Cache-Control "public, max-age=7200";
}

这将告诉浏览器缓存文件2小时,希望到那时,浏览器已经刷新了页面并获取了新文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接