How to make Angular Universal and PWA work together?

若如初见. 提交于 2019-12-04 14:38:45

问题


I have a SSR Angular app which I am trying to transform into a PWA. I want it to be server-side rendered for SEO and for the "fast first rendering" that it provides.

The PWA mode works fine when combined with SSR, but once the app is loaded, when we refresh it, the client index HTML file is loaded instead of the server-side rendered page.

I have dug into the code of ngsw-worker.js and I saw this:

// 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);
}

I have no control over this file since it's from the framework and not exposed to developers. Did anybody find a solution or workaround for this?


回答1:


I have found a working solution, the navigationUrls property of ngsw-config.json contains a list of navigation URLs included or excluded (with an exclamation mark) like explained in the documentation.

Then I configured it like this:

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

This way, none of the URLs redirect to index.html and the server-side rendered app comes into play when the app is first requested (or refreshed), whatever the URL is.

To go further, the three kinds of URLs managed by the service worker are:

  • Non-navigation URLs: static files cached by the service worker and listed in the generated ngsw.json file with their corresponding hashes
  • Navigation URLs: redirected to index.html by default, forwarded to the server if the "!/**" configuration is used
  • GET requests to the backend: forwarded to the backend

In order to distinguish a GET XMLHttpRequest from a navigation request, the service worker uses the Request.mode property and the Accept header that contains text/html when navigating and application/json, text/plain, */* when requesting the backend.

Edit:

This is actually not a good practice to do that for two reasons:

  • Depending on the network quality, there is no guarantee that the server-side version will render faster than the cached browser version
  • It breaks the "update in background" mechanism. Indeed, the server-side rendered app will always refer to the latest versions of the JavaScript files

For more details on this, please take a look at the Angular's team member answer to my feature request: https://github.com/angular/angular/issues/30861




回答2:


{
  "$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "appData": {
    "version": "1.6.0",
    "changelog": ""
  },
  "index": "../server.js",
  "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)"
        ],
        "urls": [
          "https://fonts.googleapis.com/**"
        ]
      }
    }
  ]
}

The index was described in vs code as "Specifies the file that serves as the index page to satisfy navigation requests. Usually this is '/index.html'."

So since the ssr is using a server.js file to take in requests I decided to make the "index": "../server.js" so that it backs out of the dist/browser folder and goes into the server.js file in the dist folder itself



来源:https://stackoverflow.com/questions/56383569/how-to-make-angular-universal-and-pwa-work-together

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!