qwik icon indicating copy to clipboard operation
qwik copied to clipboard

[🐞] Unneccessary prefetched scripts are all counting in Lighthouse and PageSpeed

Open fmotlagh03 opened this issue 2 years ago • 9 comments

Which component is affected?

Qwik Runtime

Describe the bug

I'm trying to get a high lighthouse/pagespeed score, and there are a lot of problems that they report which are related to scripts.

I have nothing above the fold. I just have a bunch of texts and HTML elements styled using Tailwind. I have even removed my menu icon for mobile and added a screen-tall division below my hero section to make sure that there is no script whatsoever required for above the fold.

Yet when I load my website, I see a lot of scripts being shown on the Network tab. As I searched their names, I found this line:

    <script type="module">
        document.dispatchEvent(new CustomEvent("qprefetch",{detail:{"bundles":["q-ec256a11.js","q-160ad389.js","q-2e7bf3e0.js","q-6a24878f.js","q-fc65f989.js","q-46c1b641.js","q-8628d751.js","q-98014a2f.js","q-ff5adcb4.js","q-5973ca3f.js","q-af16d7c2.js"]}}))
    </script>

This was related to npm run preview. And when I use npm run build and then serve my website using node server/entry.express.js I see this code too:

const appBundles = [["q-08ffd6c0.js", [3, 7, 9, 38], ["2x5T0jzLsAk", "5OkQdGGyL4A", "Yy6gnsbRWKY"]], ["q-0dcae3c6.js", [7, 19, 38]], ["q-10b39364.js", [7]], ["q-160ad389.js", []], ["q-161aad97.js", [7, 19]], ["q-172cd13a.js", [7, 9, 13, 19, 38], ["PdBZ3iDbPQA"]], ["q-1d05b01b.js", [7]], ["q-1d920454.js", []], ["q-1e7e6950.js", [7, 19], ["yDcyHPF1T2o"]], ["q-243bd65c.js", [7]], ["q-2ce37848.js", [7, 19, 35, 38], ["Ossmb4bjxMg"]], ["q-2da619ef.js", [7, 9, 38]], ["q-358d68f6.js", [7]], ["q-3ea0777e.js", [7, 19]], ["q-439a8f9a.js", [7, 9, 12, 38], ["A1ssYUErBQY", "RCk6LUpoQlU"]], ["q-45aab3b6.js", []], ["q-47452b66.js", [3, 7, 9, 26, 38], ["lWuAeLArgaY", "Lzju0uqPTFo", "MT4d0htQN4o"]], ["q-4a824742.js", [7]], ["q-5227d92e.js", [7, 9, 20, 38]], ["q-552e253f.js", [7], ["1QKKC0NVXEU"]], ["q-560d235e.js", [7]], ["q-565b4350.js", [7, 19], ["02wMImzEAbk", "fX0bDjeJa0E", "RPDJAz33WLA", "TxCFOy819ag"]], ["q-5fabcee5.js", [7, 9, 20, 38]], ["q-6532db9c.js", [7]], ["q-7d659ea4.js", [3, 7, 9, 26, 38], ["NJUuur5nzBE", "VV2g4riGEdk", "zF8k4Nbb9FI"]], ["q-8066be06.js", [7]], ["q-8628d751.js", []], ["q-8e413b83.js", [1, 7, 19, 38], ["H9K60qOuPGA"]], ["q-a27ce943.js", [7, 9, 12, 38]], ["q-ae4d730f.js", [7, 9, 19, 34, 38], ["dKRXGwXw2mQ"]], ["q-b5c0ff21.js", [3, 7, 9, 12, 26, 38], ["4j4plRMj3gE", "7820qma5lxY", "BUa4FGplYBI"]], ["q-bddd59fd.js", [7], ["UGxYUkCVUps"]], ["q-c2e9cded.js", [7, 19], ["AKetNByE5TM", "KnNE9eL0qfc"]], ["q-d6d97ec6.js", [7], ["vrZDxnDuCBU"]], ["q-d6dee33d.js", [7, 19]], ["q-db71af1a.js", [7, 19]], ["q-e530fb79.js", [7]], ["q-f96edf9a.js", [7], ["NyacM5awRMY"]], ["q-fc65f989.js", []], ["q-fc90b43b.js", [4, 7, 9, 19, 38], ["UdIHNtZ13Ow"]]];

Now the problem is that they affect both Lighthose and PageSpeed scores.

I see this problems in the PageSpeed report:

imageedit_0_5413915476

The are both orange warnings and it seems that they both contribute to my rather low score (between 70 and 80).

What should I do? I chose Qwik only because of this reason to get high lighthouse score.

Reproduction

I can't provide reproduction link

Steps to reproduce

No response

System Info

System:
    OS: Linux 5.19 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
    CPU: (4) x64 Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
    Memory: 10.14 GB / 15.51 GB
    Container: Yes
    Shell: 5.1.4 - /bin/bash
  Binaries:
    Node: 18.16.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 9.6.5 - /usr/local/bin/npm
  npmPackages:
    @builder.io/partytown: ^0.8.0 => 0.8.0 
    @builder.io/qwik: ^0.103.0 => 0.103.0 
    @builder.io/qwik-city: ^0.103.0 => 0.103.0 
    @builder.io/qwik-react: ^0.5.0 => 0.5.0 
    vite: ^4.3.1 => 4.3.1

Additional Information

No response

fmotlagh03 avatar Apr 25 '23 10:04 fmotlagh03

Your issue with performance is not related to prefetching scripts in the background. Notice the disclaimer below that section. It's hard to say what is causing it with no information, could be many things including poor design such as massively overusing useVisibleTask$, etc. What certainly doesn't help is the ~600ms latency, hence why that one is colored red.

Like I said though, hard to tell without any information at all, but scripts need to be downloaded at some point, when do you think that should be? Hopefully not when the user wants to use them, otherwise they're waiting, bad experience. But since they're offloaded to the service worker to prefetch, they are ready when needed, and the impact on performance score should be negligible.

jordanw66 avatar Apr 25 '23 20:04 jordanw66

This is a known issue, I hope it will be fixed soon. I have halted all development using qwik until this has been resolved. It's Qwik's premise to have 0kB on load, without it you may as well use any other framework.

https://github.com/BuilderIO/qwik/issues/3756

Dindaleon avatar Apr 26 '23 12:04 Dindaleon

@Dindaleon That issue is not related, that is regarding an increase in a specific bundle size. Qwik is not meant to download 0kB of JS on load and I am not sure where you heard this but it's a misunderstanding. It is supposed to be O(1). It actually loads 1kB inlined in the HTML to begin with (and always has) and only loads other JS when needed, however it will still prefetch scripts for the page in the background so that it is ready in the cache for when it needs to be loaded.

jordanw66 avatar Apr 26 '23 12:04 jordanw66

@Dindaleon That issue is not related, that is regarding an increase in a specific bundle size. Qwik is not meant to download 0kB of JS on load and I am not sure where you heard this but it's a misunderstanding. It is supposed to be O(1). It actually loads 1kB inlined in the HTML to begin with (and always has) and only loads other JS when needed, however it will still prefetch scripts for the page in the background so that it is ready in the cache for when it needs to be loaded.

1kB inline sure, no problem! But no 50kB (in three files. Even for a blank page loading unnecessary ~50kB of JS is ridiculous). I have versions of Qwik where I load no javascript at all and I hope future versions will do the same. I have seen youtube videos showing this and it's pretty awesome. I don't understand why it would change. It's the one feature I like most.

Dindaleon avatar Apr 26 '23 15:04 Dindaleon

@Dindaleon Yes the one bundle size is an open issue but the versions of Qwik which didn't prefetch script bundles in the background on the worker thread were actually broken, that was an issue that was fixed.

jordanw66 avatar Apr 26 '23 16:04 jordanw66

So, we won't have no JS load?

Dindaleon avatar Apr 26 '23 18:04 Dindaleon

There's going to be some JS, but it is very minimal and loaded on demand. If you have scripts in the page they will be prefetched in the background.

Without prefetching, when a user goes to interact with your page, they will need to wait until the scripts are downloaded. This will feel horrible for the UX. This lack of prefetching observed in some previous versions was actually a bug that got fixed.

@Dindaleon Legitimate question, when do you think scripts should be downloaded in the background? Do you think the user should wait ~500ms on a mobile connection for a button click to download the script before the action happens, like opening a hamburger menu for example? Or should the page become active immediately and start prefetching that script in the background right away so that it's ready when the user clicks?

jordanw66 avatar Apr 27 '23 00:04 jordanw66

I understand your point and of course i want immediate action when a user clicks something; however, I would like the scripts to be loaded after the first load, not with it (I don't know if that is how it actually is working at the moment). The way you are putting it sound like that, if so, that's good I guess. I do not want to load ~50kB on the first load, nor when a page is practically a blank page.

Dindaleon avatar Apr 27 '23 20:04 Dindaleon

@jordanw66 I just updated to the latest version and it is beautiful! :heart:

Dindaleon avatar Apr 28 '23 13:04 Dindaleon

Hey @fmotlagh03, thanks for opening this issue, is it still a valid issue with the latest version of Qwik?

gioboa avatar Sep 14 '23 13:09 gioboa

This seems solved. So I'm closing this issue for now, feel free to re-open it if it's still an issue for you.

gioboa avatar Jan 24 '24 22:01 gioboa

image Same here, @builder.io/qwik": "^1.4.5"

// PostView 
import { Resource, component$ } from "@builder.io/qwik";

import { useFetchUrl } from "~/routes/[slug]";

export default component$(() => {
  const PostData = useFetchUrl();

  return (
    <Resource
      value={PostData}
      onPending={() => <div>loading...</div>}
      onRejected={(err: any) => (
        <p>looxi roo {err.status ? err.status : err.message} </p>
      )}
      onResolved={(res) => <pre class="mb-10">{JSON.stringify(res)}</pre>}
    />
  );
});


// [slug]/index.tsx
export const useFetchUrl = routeLoader$(async (requestEvent) => {
  const slug = requestEvent.params.slug;
  const slugRes = await fetch(`http://localhost:3009/api/v1/slugs/${slug}`);
  const slugResJson = await slugRes.json();

  if (slugRes.ok) {
    if (slugResJson.redirect) {
      throw requestEvent.redirect(301, "/" + slugResJson.redirect);
    }

    if (slugResJson.type == "news") {
      const postRes = await fetch(GetPostDetailEndpoint(slug));
      const resJson = await postRes.json();

      if (postRes.ok) {
        return {
          ...resJson,
          content: JSON.parse(resJson.content),
          type: slugResJson.type,
        };
      }

      throw {
        status: postRes.status,
        response: resJson,
      };
    }
    if (slugResJson.type == "category") {
      const postRes = await fetch(GetPostByCategorySlugEndpoint(slug));
      const resJson = await postRes.json();

      if (postRes.ok) {
        return { ...resJson, type: slugResJson.type };
      }

      throw {
        status: postRes.status,
        response: resJson,
      };
    }
  }
  throw {
    status: slugRes.status,
    response: slugResJson,
  };
});

export default component$(() => {
  const data = useFetchUrl();
  return <>{data.value.type === "news" ? <PostView /> : <null />}</>;
});


In index.tsx it oke, but [slug] first load 50kb js

ducmaster01 avatar Feb 27 '24 21:02 ducmaster01

@nhayhoc can you give more details? What is in those 50kb? Why are they being loaded? Do you have useVisibleTask that triggers loading the framework early?

wmertens avatar Feb 28 '24 07:02 wmertens