如何让 Angular Universal 和 PWA 协同工作?

How to make Angular Universal and PWA work together?

我有一个 SSR Angular 应用程序,我正试图将其转换为 PWA。我希望它是为 SEO 和它提供的 "fast first rendering" 呈现的服务器端。

PWA 模式在与 SSR 结合时工作正常,但是一旦加载应用程序,当我们刷新它时,将加载客户端索引 HTML 文件而不是服务器端呈现的页面。

我深入研究了 ngsw-worker.js 的代码,我看到了这个:

// Next, check if this is a navigation request for a route. Detect circular
// navigations by checking if the request URL is the same as the index URL.
if (req.url !== this.manifest.index && this.isNavigationRequest(req)) {
    // This was a navigation request. Re-enter `handleFetch` with a request for
    // the URL.
    return this.handleFetch(this.adapter.newRequest(this.manifest.index), context);
}

我无法控制此文件,因为它来自框架并且未向开发人员公开。 有人为此找到了解决方案或解决方法吗?

我找到了一个可行的解决方案,ngsw-config.jsonnavigationUrls 属性 包含包含或排除的导航列表 URL(带有感叹号),例如在 the documentation.

中解释

然后我这样配置:

"navigationUrls": [
    "!/**"
]

这样,URL 中的 none 会重定向到 index.html,并且 server-side 呈现的应用会在首次请求(或刷新)应用时发挥作用, 无论 URL 是什么。

更进一步,service worker管理的三种URL是:

  • Non-navigation URLs:由 service worker 缓存并在生成的 ngsw.json 文件中列出的静态文件及其相应的哈希值
  • 导航URLs:默认重定向到index.html,如果使用"!/**"配置则转发到服务器
  • GET向后端请求:转发给后端

为了区分 GET XMLHttpRequest 和导航请求,service worker 使用 Request.mode 属性 和 Accept header 在导航时包含 text/html,在请求后端时包含 application/json, text/plain, */*

编辑:这实际上不是一个好的做法,原因有二:

  • 根据网络质量,无法保证 server-side 版本的渲染速度会比缓存的浏览器版本快
  • 它打破了“后台更新”机制。事实上,server-side 呈现的应用程序将始终引用 JavaScript 文件的最新版本

有关这方面的更多详细信息,请查看 Angular 的团队成员对我的功能请求的回答:https://github.com/angular/angular/issues/30861

更新 v11.0.0

Angular 现在有一个 navigationRequestStrategy 选项,可以优先考虑服务器的导航请求。更新日志摘录:

service-worker: add the option to prefer network for navigation requests (#38565) (a206852), closes #38194

要明智地使用!此警告出现在文档中:

The freshness strategy usually results in more requests sent to the server, which can increase response latency. It is recommended that you use the default performance strategy whenever possible.