如何在 Angular 5+ Service Workers 中处理路由?

34
在先前版本的Angular服务工作者实现中,其中一个配置选项是"routing"。这可以在未回答的SO问题中看到,在Angular CLI问题中引用,并且最好的剩余文档似乎是由Angular团队的Stephen Fluin发布的博客文章,以及来自Google的Alex Rickabaugh的I/O讲座
使用Angular 5,ServiceWorkerModule已经得到很好的建设,大部分配置现在都可以使用ngsw-config.json文件处理。然而,在angular.io指南文档中再也没有提到如何处理路由重定向。因此,我遇到了以下问题:当我访问我的应用程序并离线时,仍然可以直接访问该应用程序:https://jackkoppa.github.io/cityaq-sw-issue。但是,在加载应用程序后,应用程序重定向到search路由,大多数用户将尝试从类似https://jackkoppa.github.io/cityaq-sw-issue/search?cities=Shanghai这样的URL加载(现在只谈论Chrome桌面和移动端,并在可能的情况下使用无痕模式)。
当您在离线状态下尝试访问该URL时,立即会收到504 - 网关超时的错误。这是因为服务工作者仅缓存了索引,并不知道其他路由应该重定向到索引,以便它可以加载。我相信Angular服务工作者实现的先前迭代可以通过设置重定向到给定路由的索引来处理此场景。当前的Angular 5+ ngsw-config.json文件或生成的ngsw.json文件中是否有处理此重定向的方法?如果没有,应如何在单独的服务工作者JS文件中处理解决方案?

我真的很想知道这个问题的答案。 - Stephen Agwu
@stephenagwu同意。我无法在任何地方找到文档,这让我觉得我可能需要在Angular存储库上将其写成问题。希望在那之前有人对服务工作者有更好的理解。 - Jack Koppa
2
我在第一次注意到这个行为时就打开了一个问题。像你说的那样,在v5之前它运行得很好。然而,当涉及到SW时,我不确定问题是出在Angular还是CLI上(它构建ngsw-worker.js)。我已经在Angular存储库https://github.com/angular/angular/issues/21636上打开了它。 - MrCroft
我已创建了a repo,它与@MrCroft在上面提到的问题中发现的最小复制步骤相匹配。仍在尝试挖掘ngsw-worker.js在获取过程中失败的位置。 - Jack Koppa
将赏金过期,因为感觉我们没有得到关于Angular团队期望的完整答案。希望我可以按照我的回答继续处理拉取请求,或者有更多知识的人偶然发现这个问题。 - Jack Koppa
2个回答

11

TL;DR:我已经确定了至少两个导致我的情况出现故障的问题;您现在可以使用此构建脚本尝试我的修复,并在此处查看应用程序的离线工作。需要进一步测试和拉取请求。


虽然我无法找到文档答案,但在查看ngsw-worker.js文件和阅读@angular/service-worker提交历史时,我已经找到了一些信息。新的ServiceWorkerModule“路由”方法是将任何“导航”请求(即同一域上的非索引路由)重新运行handleFetch方法,但现在指向index.html请求(source)。这应该可以正常工作,因为SPA应该能够在检索到其索引的缓存文件后重定向到URL。
因此,导致我的网站离线时失败的问题必须与路由无直接关系,我目前已经发现了两个问题。通过解决这些问题的变通方法,我已经让我的应用程序基本按预期工作。在原始问题中的复制步骤中,cityaq现在正在工作,而我已经在cityaq-sw-issue上复制了原始失败的网站。 这些问题:
  1. 托管版本的Angular应用程序,在CLI中设置了--base-href标志时,会为其服务工作者资源列出绝对URL。当比较对这些资源的请求时,ngsw-worker.js期望相对URL

    • source
    • 我非常有信心需要解决这个问题,并且我将致力于修复拉取请求。这是因为在生成ngsw.json时,baseHref会被包含在资源URL中(source
  2. 在我的经验中,ngsw-worker.js可以处于3种可用的“状态”之一,EXISTING_CLIENTS_ONLY似乎设置不正确。

    • source 1, source 2
    • 我对此变化的信心较低,因为我无法始终看到导致此状态的原因。但是,如果服务工作者进入此状态,ngsw-worker.js将不再尝试检索缓存的资源(source),在我的测试中,即使唯一的更改是断开互联网连接,应用程序仍然进入此状态,这似乎是“正确”的情况

我正在为问题#1准备一份公关文件,并等待一些帮助来理解问题#2,我正在使用一个临时的构建脚本来修改生成后的ngsw-worker.js。警告:这很丑陋,肯定会修改设置EXISTING_CLIENTS_ONLY的预期“安全”行为。但是,对于我的应用程序,在本地运行(http-server)和部署到实时URL(在我的情况下是GitHub页面)时,它确实允许正确的离线性能。

使用此解决方法: (Angular 5,已测试w / Angular 5.2.1)

  1. npm install replace-in-file --save-dev
  2. 此脚本包含在您的应用程序中,例如位于build/fix-sw.js
  3. 在您的package.json中:
"scripts": {
   ...
   "sw-build": "ng build --prod && node build/fix-sw",
   "sw-build-live": ng build --prod --base-href https://your-url.com/ && node build/fix-sw"
   ...
}
  1. 要本地运行

npm run sw-build
cd dist
http-server -p 8080

  1. 在将构建文件发送到服务器之前,为您的实际 URL 准备一个构建(例如使用angular-cli-ghpages

npm run sw-build-live


2
好的,我不会花太多精力来解决构建脚本中的504问题,直到我们看到那个PR如何影响这个问题。 https://github.com/angular/angular/issues/21636 也值得关注以获取任何其他更新。 - Jack Koppa
3
这个修复在Angular 6中仍然需要吗?我正在使用Angular 6.0.8,在离线模式下遇到了504错误。 - Josh Jobin
1
@JoshJobin - 这是一个很好的问题,我希望不需要这样做。或者至少,如果还有Service Worker的问题,它们已经不再是这两个了。我已经几个月没有回到我的cityAQ项目了,但我创建了一个问题来升级到Angular 6,以便我可以在没有构建脚本的情况下测试出什么正在工作。希望在8月份时有更新,当时我会有更多时间来处理这个项目。与此同时,希望其他人能够提供比我更近期的答案? - Jack Koppa
@JackKoppa,我所看到的证据表明Angular 6并没有解决这个问题。 - PeterS
就像这样,它在 stack-overflow 的末尾:))。感谢您的帮助,看起来您的修复在本地有效,第二个问题非常奇怪,我认为需要多次解决,我无法在本地重现它,但当我在生产环境中运行时,它到处都是,同时 ng sw 文档需要更新。我很有兴趣帮助解决这个问题,您可以随时通知我,或者如果您没有时间,也许我们可以讨论一下问题,我可以帮助您组合一个 pr。 - Nicu
显示剩余2条评论

0

我刚刚花了几天时间处理这个问题,虽然我没有使用路由,只是一个非根基础 href。我将分享适用于我的配置,它至少有可能适用于路由应用程序,因为每当我尝试使用 route/ngsw/state 进行调试时,我都会得到我的应用程序。

  1. 这是主站的“子应用程序”,直接链接到 http(s)://<host>/route/index.html
  2. manifest.webmanifest 仅提供服务,不知道或关心 angular 对事物的处理方式,因此显式地包含了子路由。
    "scope": "/route/",
    "start_url": "/route/index.html",
    
  3. angular.json 已设置 baseHref,因为整个应用程序都在此下提供服务,这就是 base-href 的用途:
    "baseHref": "/route/",
    
  4. ngsw-config.json 是比较棘手的一个,因为它使用其模式在部署文件夹中查找要添加到 ngsw.json 的文件,所以添加 baseHref 或使用相对路径等内容会破坏它。该文件需要像从根目录提供服务那样思考和查看,例如:
    "index": "/index.html",
    ...
      "files": [
        "/index.html",
        "/*.js"
      ]
    
  5. Angular CLI 将在 ngsw.json 中添加 baseHref,为您提供像 "/route/index.html" 这样的资源 URL 和哈希键。
  6. 由于 baseHref 的原因,您的应用程序将发送像 "/route/index.html""/route/main.js" 这样的请求,它们将与它们被哈希存储的键匹配,并且现在实际上已经被缓存。
  7. 服务工作线程已正确地注册在 /route/ 下,因此它将尝试处理请求并在您离线时在其缓存中找到它们。
其中让这件事情变得非常困难的一点是有很多不同的路径和基础指定方式,而且有些非常灵活,而另一些则非常严格。如果您的部署出现问题,您实际上无法使用内置的调试功能,也不能像使用合理模块那样打开调试日志记录。与指令和模板等内容相比,文档非常简洁。
尽管如此,这比我找到的任何其他解决方案都容易1000%。
顺便说一下:如果您使用ServiceWorkerModule.register('ngsw-worker.js', { enabled: true })并将"serviceWorker": true,"ngswConfigPath": "ngsw-config.json"移到angular.json中的一般构建选项而不是在生产配置下,您就不必等待额外的5分钟来进行调试。
附言:感谢 OP's sleuthing,它使我的断点处于正确的位置。

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