'Unexpected token <' on every new build of angular production PWA until site refresh

后端 未结 6 1361
夕颜
夕颜 2021-02-13 00:35

I know there are some similar questions like so unexpected token after angular 2 production build but it doesnt actually answer my question.

Basically I have an angular

6条回答
  •  执念已碎
    2021-02-13 01:01

    The issue is caused by the installed ServiceWorker, which is the one that interferes and caches all responses, including the ones that you have just updated.

    More information about service workers: MDN - Using Service workers

    https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers

    That is why window.location.reload() does not work, because it does make a new request but this request is hijacked by the installed ServiceWorker and instead of letting the real request go through to the real server and fetch the real response (updated main.js and assets), it just returns back to your app a cached response, which is the old main.js and not the updated one and that is why it fails.

    More specifically the ServiceWorker is caching the main.js and other assets, which are generated each time you change your code. Main.js when loaded in turn it lazy-loads (makes an http request to) other chunks of your application (e.g. the 5.xxxxx.js).

    When you do make a change in your code, and even though all your chunks gets updated, including main.js and the 5.xxxxxx.js chunk, the browser is still running the older main.js. This older main.js has a reference to the older pair of 5.xxxxxx.js which no longer exists. At this point if you do a reload() programatically, the installed ServiceWorker responds with the older cached main.js which tries in turn to lazy-load the older version of 5.xxxxx.js from the server which does not exist and therefore gets a response of an 404 html error. Hence the '<' token exception (it's actually the first character of the tag of the 404 error page).

    This is evident from the network panel screenshot. If you examine the screenshot you will see that the main.js and all other assets are delivered from (from service worker), which means that those are the cached older versions and are different from the actual updated files that now exist in your web server.

    To fix this issue, the ServiceWorker must be configured to not cache the main.js and instead always let this request to go through to the real server in order to fetch any updated version of main.js that in turn will lazy-load the updated 5.xxxx.js chunk that now actually exist in the web server.

    Here is how you can configure the ngsw-config.json of ServiceWorker to specifically not cache the main.js file (assuming it is of the pattern main.xxxxxxx.js) in order to fix this issue:

    {
      "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 ng build --prod to rebuild your app and test it.

    UPDATE:

    To make sure that this is not a browser caching issue, pass true as the first argument to the window.location.reload() method. This instructs the reload to be executed without cached assets by the browser:

    root.component.ts

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

    UPDATE 2:

    According to the documentation of Angular, the Service Worker that is installed already on browsers of clients is cached. If you update the Service Worker code (like you did recently), then the new Service Worker code must be updated in all the browsers of the clients that have it already installed. Angular documentation states that there is a periodic check, but it doesn't specify it, that will check if the Service Worker itself needs an update and will do it. https://angular.io/guide/service-worker-devops#service-worker-updates

    Perhaps, the manefistation of the issue only in the production and not in development or with a Private Browser mode (clean browser slate with no SW installed), seems that this is the case.

    To verify this, you should compare the existing browser's SW that is running on the page using the developer tools of Chrome, with the updated SW code that should be installed. If they are different then it means that it hasn't been updated yet and that's what's causing the issue.

    You can do this using a chrome browser that exhibits this issue and by going to Chrome Dev tools -> Application Tab -> Service Workers (in the top of left panel) and there will be the SW that is registered in the browser. Click the Source link and it will open up the actual Code of the SW. Compare the code line by line (or use a diff utility) with the updated SW code that should be running.

提交回复
热议问题