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

后端 未结 6 1588
半阙折子戏
半阙折子戏 2021-02-13 00:26

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 00:55

    I have one solution for this issue. For get https://angular.io/cli/build

    get your app angular.json file. And change outputHashing value all to media

               "architect": {
                    "build": {
                        ...
                        "configurations": {
                            "production": {
                                ...
                                "outputHashing": "media", // "all"
                            }
                        }
                    }
                }
    
    0 讨论(0)
  • 2021-02-13 01:02

    I'm no expert to angular problem. The best I can do is help you break down the problem, help you debug.

    1. If you just want the error go away (and you have control over the server that serves your index.html), I'm pretty sure it can solved by setting these http response headers (source):
    Cache-Control: no-cache, no-store, must-revalidate
    Pragma: no-cache
    Expires: 0
    
    1. However, such setup more or less beats the point of PWA in the first place. PWA allow users to use your site even offline, they must be able to get to index.html first, so index.html has to be cached.

    2. Caching index.html is the right thing to do. It's likely your cache-control setting for your old .js and other assets has some problems. I'm not sure which part goes wrong, but the expected behavior is: these assets should be cached just like index.html, 404 should never happen in first place since browser should read from local cache.

    3. One thing you can do to quickly "cover up" the problem, is simply keep all the old assets of previous version on server too. This way, cached index.html ref to old assets, they remain accessible, no 404 thus no "unexpected token <".

    4. Now I would suggest you try point 4 first, to verify my theory at point 3 is correct. Also check if SwUpdate works properly. Ideally, you should see following:

      1. you publish newer version of code
      2. you visit the site, should see cached older version
      3. after older version of app is loaded, SwUpdate automatically refreshes the page for you and load the newer version.

    To sum up, my theory is things go wrong at point 3. Older assets should be correctly cached, using proper http cache-control headers (or angular service worker has other way of doing same things? I'm not familiar with that part). If you cannot figure it out, then just keep the old assets accessible on server.

    0 讨论(0)
  • 2021-02-13 01:03

    Double check your cache headers. Chrome doesn't always listen to no-cache. If you've visited the site recently chrome will load index.html from cache. Causing the error.

    We've changed our index.html to no-store and that seemed to solve the issue. https://gertjans.home.xs4all.nl/javascript/cache-control.html

    0 讨论(0)
  • 2021-02-13 01:09

    I have experienced this error when lazy loading modules.

    It was an Angular / Iconic PWA and was related to a missing import:

    import { SharedModule } from '@shared/shared.module';
    

    Also see:

    • Angular 2. Error: Loading chunk failed
    • How to overcome loading chunk failed with Angular lazy loaded modules
    0 讨论(0)
  • 2021-02-13 01:14

    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.

    0 讨论(0)
  • 2021-02-13 01:19

    This usually happens for me when the requested resource returns 404 and instead of requested JS file you receive your index.html which has tags hence '<' token. Could it somehow be the case? Say you have versioning on your scripts and rebuild invalidates old URLs or something like that.

    0 讨论(0)
提交回复
热议问题