Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nuxt 3 prefetch all assets from every pages and every layouts. #13778

Closed
kstraszewski opened this issue Apr 20, 2022 · 39 comments · Fixed by nuxt/framework#6210
Closed

Nuxt 3 prefetch all assets from every pages and every layouts. #13778

kstraszewski opened this issue Apr 20, 2022 · 39 comments · Fixed by nuxt/framework#6210

Comments

@kstraszewski
Copy link

kstraszewski commented Apr 20, 2022

Environment

Nuxt CLI v3.0.0-rc.0
Nuxt project info:


  • Operating System: Darwin
  • Node Version: v16.14.0
  • Nuxt Version: 3.0.0-rc.0
  • Package Manager: npm@8.3.1
  • Builder: vite
  • User Config: css
  • Runtime Modules: -
  • Build Modules: -

Reproduction

https://github.com/kstraszewski/nuxt3-playground/tree/prefetching-all-assets

Also we have qa version of our project here:
https://b2c.qa2.lendi.pl/design

Describe the bug

Nuxt after build prefetch all the assets like CSS, fonts, media, imgs from all the pages and all the layouts. As you can see in the repo example you have /pageA which implement layout-a, with some video in the pageA.vue. Also, you have /pageB which implements layout-b which loads popular @mdi/font.

The problem I have is it doesn't matter on which page you go nuxt always loads all the assets. When you go to /pageA you will see the mdi fonts from pageB and layoutB also are prefetching by the nuxt:

PageA:
image

PageB:
image

Both pages Loads a lot.

My guess is that vite builder prefetch every URL that he find in CSS or template.

Additional context

It makes nuxt unusable in production mode because It hurts LCP very badly:

As you can see on the example below we have huge LCP (I've made some tricks witch mdi/fonts, but this is very ugly solution) with fully loaded mdi/fonts I've received 27 sec of LCP.

https://pagespeed.web.dev/report?url=https%3A%2F%2Fb2c.qa2.lendi.pl%2Fdesign

Logs

No response

@Foddy

This comment was marked as duplicate.

@kkauper

This comment was marked as duplicate.

@tomekhub

This comment was marked as duplicate.

@AleksanderWojtkowski
Copy link

Same here ...

@engvie
Copy link

engvie commented May 12, 2022

I have a reproduce here:

https://stackblitz.com/edit/github-krh2ru?file=layouts%2Fadmin-layout.vue

I don't really understand why this is a minor bug when it's almost impossible to use the layouts and components are sharing styles.

@danielroe
Copy link
Member

@engvie I would advise against using global styles in any case, particularly ones targeted at elements like html and body. Even when this issue is resolved, you will continue to experience problems with CSS specificity and styles conflicting with each other. Instead, you should be using scoped styles, deliberate global class names, or CSS modules.

@engvie
Copy link

engvie commented May 12, 2022

@danielroe I agree they should be scoped.

Maybe there is a better solution for what I am trying to do.
In my case I want to separate my styles for the administration and the "frontend".

In my admin layout I want to import admin.scss. In my frontend layout I want to import app.scss.

@kstraszewski
Copy link
Author

I think this is not the same problem @engvie. My point is that app after building and deploying prefetch for example all the images from all the pages and all the layouts. So when I deploy my Landing Page with 0 images it prefetches all the images from the rest of the pages. And it critically lengthens the LCP metric. But I agree it is not minor because it makes deploying an app with lots of pages and lots of images pointless :) I've got like 20 sec of LCP on my page when the other metrics are great.

Finally we workaround it by temporarily removing videos on the page and we are waiting for the fix to come.

@engvie
Copy link

engvie commented May 13, 2022

@kstraszewski
Maybe it's not the same issue or maybe a part of it.

I have 2 layout files:

  • Default.vue <-- import '~/assets/scss/app.scss'
  • AdminLayout.vue <-- import '~/assets/scss/admin.scss'

Both styles are included at both layouts. The admin style are destroying the default layout style.

I don't know if there is another way to separate the styles? I think this is an issue?

@kstraszewski
Copy link
Author

Ok so I think this is part of this issue. It would be great to have layout css scoped to all the pages that are child of that layout, but I didn't test adding scoped to layout css.

This style part of that bug is not bothering me much, coz css doesn't weight lots of kb, but imgs and videos are.

@juliendargelos
Copy link

Has someone already identified what causes this problem ?

@dungonfq
Copy link

dungonfq commented Jun 3, 2022

We're facing the same issue #14093. On production, it mets our server's rate limit so some last assets couldn't be loaded (403 Forbidden)

@leosin
Copy link

leosin commented Jun 9, 2022

code

so,should i put the fonts in public directory?

@Renhor
Copy link

Renhor commented Jun 10, 2022

@danielroe the idea of writing styles in scoped / css modules is great. However, the problem affects all the Assets, which makes it problematic to deploy large applications. For example, the size of downloaded assets on one of my pages in Nuxt2 is ~700kb, when in Nuxt Bridge it reaches ~3mb on the same page.

P.S. The bug is also reproduced in Nuxt Bridge + webpack.

@kstraszewski
Copy link
Author

kstraszewski commented Jun 19, 2022

@danielroe is there any workaround for this bug? I'm migrating my projects to Nuxt 3. The size of every page is great, we are getting 2x smaller bundler compared with nuxt2. But this bug is killing FCP and LCP on the larger project.

I found a workaround for images, you can put images to public and then Nuxt doesn't load them at all, but I can't find a workaround for the rest of the pages (js + CSS) and unfortunately nuxt loads all of them.

Using new Performance Insight devtool tool for chrome it shows that when I go to PageA (for example: https://b2c.qa2.lendi.pl/ for debug) FCP is loaded after nuxt prefetch all of js and CSS from the other pages (for example, prefetch js and CSS from this page https://b2c.qa2.lendi.pl/kredyty-hipoteczne which is under development, but unfortunately burdens rest of the production pages :/ )

For my opinion (correct me if I'm wrong) Nuxt3 lacks Smart prefetching from Nuxt2 is this feature planned for a stable release or rather its matter of 3.1 version?

@edit:
It may be helpful for debugging. Here is a stable version in nuxt2 of the subproject above:

https://www.lendi.pl/kredyty-hipoteczne/

The current version linked above (https://b2c.qa2.lendi.pl/kredyty-hipoteczne) is under development, and written in Nuxt3. We achieved a 3.5x smaller bundle 700kb vs 2.5mb, but exactly the same https://pagespeed.web.dev/ performance score, coz huge FCP and LCP. I know I can go with CLS to 0 and polish some things, but with this FCP and LCP I can't achieve good perf :/

@mmis1000
Copy link
Contributor

mmis1000 commented Jun 22, 2022

I think the error lies in

https://github.com/nuxt-contrib/vue-bundle-renderer/blob/c1c6d74e4f20fd75743bb0882efdf47c137ac721/src/renderer.ts#L313

And

https://github.com/nuxt-contrib/vue-bundle-renderer/blob/c1c6d74e4f20fd75743bb0882efdf47c137ac721/src/renderer.ts#L274

The bundle renderer tries to prefetched 1. [assets of entrypoint, used module] and 2. [assets of dynamic dependency of entrypoint, used module].

However, dynamic dependency of entrypoint contains every route . with in turn cause all assets of all routes to be prefetched.

IMO. The fix about this could either be

  1. just don't prefetch any dynamic dependency of entrypoint.
  2. prefetch dynamic dependency of entrypoint but don't include assets.
  3. add a blacklist of entrypoint dynamic dependency to the bundle renderer (So you can exclude just route while keep things used by layout prefetched.)

Edit:

It seems the manifest itself is also incorrect. The entry somehow contains all image assets of all routes as dependency while page themselves contains none.

@danielroe
Copy link
Member

Thanks for the helpful summary @mmis1000.

There is a PR open that does not prefetch prefetches of dynamic dependencies: nuxt-contrib/vue-bundle-renderer#30.

You are also right that the manifest is incorrect; this seems to be an issue with vite or the vite config within nuxt that we'll need to resolve as well.

@mmis1000
Copy link
Contributor

mmis1000 commented Jun 22, 2022

Thanks for the helpful summary @mmis1000.

There is a PR open that does not prefetch prefetches of dynamic dependencies: nuxt-contrib/vue-bundle-renderer#30.

You are also right that the manifest is incorrect; this seems to be an issue with vite or the vite config within nuxt that we'll need to resolve as well.

After a bit of inspection, the image module are actually bundled alongside the entry point. So the manifest is technically correct.

So the actual question is:
Why are the asset modules bundled with entrypoint?

@mmis1000
Copy link
Contributor

mmis1000 commented Jun 23, 2022

@danielroe

I think I located the reason that cause urls to be bundled into entry chunk. It's caused by nuxt:dynamic-base-path.
The rewritten asset chunk emitted by that plugin has direct dependency against entry chunk. And cause vite(rollup?) to decide to merge assets chunk into it instead.

Insert a return under this line https://github.com/nuxt/framework/blob/e38de4af4dd98705a226b551fa9d6729d7b1a130/packages/vite/src/plugins/dynamic-base.ts#L69 makes nuxt generate proper manifest instead of a broken one.

By the way, the generated script from that plugin seems to be also incorrect. It missed a /*#__PURE__*/ after export, which caused it to be always imported even binding is not used when forced into own chunk with manualChunks.

I wonder if you rewrite the one that import the asset instead of rewrite asset chunk itself, would this still happen?

Cause that,
If you rewrite the chunk that imports asset.
Then the "#nitro/paths" is no longer a dependency of asset chunk.
And the chunk merging should no longer be messed up?

@mmis1000
Copy link
Contributor

Made a patch set to experiment idea above, and it do work (on my machine).
The built server output prefetch with exactly what is used now.

https://gist.github.com/mmis1000/3a5882636a340df8e67b84099a9364ad

Paste here for who interested.

@kstraszewski
Copy link
Author

kstraszewski commented Jun 24, 2022

Ok I think it works after I locally patched node_modules and running npm run build. App doesn't loads additional mjs and CSS files, It saves me +-100kbs so it's nice. (Resources loaded 1.6mb vs 1.5mb) But it still doesn't do huge change in LCP and FCP for me :/

Without patch:
image

With patch:
image

@Aleksandr-om
Copy link

Aleksandr-om commented Jul 4, 2022

@danielroe I see that this problem has been known for several months.
I have a few questions:

  • Is there any solution to make preloading work properly? It is advisable not to disable it, as @mmis1000 suggested.
  • Is this issue planned to be resolved in the coming months?

@danielroe
Copy link
Member

There is a difference between preload and prefetch. There is an issue awaiting review that prevents over-zealous prefetching: nuxt-contrib/vue-bundle-renderer#30.

There is another issue which is that the vite manifest that is built includes too many assets as direct dependencies of the entry point.

Yes, this is planned to be resolved. Likely we need to merge nuxt/framework#5398 first.

@kosmeln
Copy link

kosmeln commented Jul 25, 2022

We can't wait to see this fixed sooner! Fingers crossed!

@HochinChan
Copy link

HochinChan commented Aug 18, 2022

Has the problem been solved? I had the same problem.

image

@kstraszewski
Copy link
Author

kstraszewski commented Aug 18, 2022

Yea I still have this issue also, but it will be allowed in the future:
nuxt/framework#6210 (comment)

@danielroe danielroe added the 3.x label Jan 19, 2023
@danielroe danielroe transferred this issue from nuxt/framework Jan 19, 2023
@mirash333
Copy link

Any update ?

@manniL
Copy link
Member

manniL commented Feb 3, 2023

@mirash333 when an issue is closed, it usually won't receive any updates. Feel free to follow along #18376

@matthew-dean
Copy link

@mirash333 Are you still having issues? I opened this bug. #22817

@rathahin
Copy link

Production is a real challenge for me. Why is it necessary to thoroughly scan all assets, and why can't we make updates directly in the production environment? Why does even a minor change to a single image require a full rebuild?

@anthonypillot
Copy link

Idk if it can help, but if you are using vite and Nuxt 3, you can check this solution:

  vite: {
    build: {
      cssCodeSplit: false,
    },
  },

If you'd rather have all the CSS extracted into a single file, you can disable CSS code splitting by setting build.cssCodeSplit to false.

However, with webpack (Nuxt 2) :

  webpack: {
    extractCSS: true,
  }

@beom-lk

This comment was marked as off-topic.

@carbdias
Copy link

I would like to know if this is possible in Nuxt 3 :

router: {
prefetchLinks: false
}

@hamedniroumand
Copy link

hamedniroumand commented Feb 19, 2024

I have the same issue with nuxt bridge and vite.


env

  • ssr: false
  • target: static

@marcos-vinicius-dev
Copy link

marcos-vinicius-dev commented Mar 6, 2024

I have the same issue with nuxt bridge and vite.

env

  • ssr: false
  • target: static

@hamedniroumand I have the same problem. Did you get a solution?

@jschroeter
Copy link

Having the same issue, some video files get prefetched. I would like to prevent that as the videos are initially not visible on the page and only gets visible once the user clicks the preview. Also the user only needs one of the file formats, not mp4 and webm.

The component on the page has a slot where we provide the video sources:

But the VideoOverlay component only renders these when the user clicks. Meaning the SSR-rendered HTML doesn't include the <source> tags, but still the files get prefetched:

Copy link
Member

@jschroeter You can use the build:manifest hook to disable preloading/prefetching of those resources.

@myl303
Copy link

myl303 commented Apr 25, 2024

nuxt3,nuxt.config.ts.Just have a try,It is work for me.

hooks: {
"build:manifest": (manifest) => {
for (const key in manifest) {
const file = manifest[key];
// console.log("-----", file);
//all file stop prefetch;
file.preload = false;
file.prefetch = false;
}
},
},

@jschroeter
Copy link

Yes, that works, thanks.

But I have the feeling it would be better if the Nuxt prefetching behavior would be adjustable from e.g. inside the template. E.g. look at this example: https://stackblitz.com/edit/nuxt-starter-qw2rgq?file=app.vue

    <picture>
      <source srcset="~/assets/nuxt.avif" type="image/avif" />
      <source srcset="~/assets/nuxt.webp" type="image/webp" />
      <img src="~/assets/nuxt.png" />
    </picture>
    <video>
      <source src="~/assets/nuxt.mp4" type="image/mp4" />
      <source src="~/assets/nuxt.webm" type="image/webm" />
    </video>

which results in

<link rel="prefetch" as="image" type="image/png" href="/_nuxt/nuxt.BkOrghPF.png">
<link rel="prefetch" as="video" href="/_nuxt/nuxt.DwnW1OXI.mp4">
<link rel="prefetch" as="video" href="/_nuxt/nuxt.Kwi2KNf4.webm">

In this example it doesn't make sense that the PNG image and both videos are prefetched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.