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

application builder generates many initial chunks #26307

Closed
jnizet opened this issue Nov 9, 2023 · 21 comments
Closed

application builder generates many initial chunks #26307

jnizet opened this issue Nov 9, 2023 · 21 comments

Comments

@jnizet
Copy link
Contributor

jnizet commented Nov 9, 2023

Command

build

Description

I just migrated a small project to v17 and the application builder.
The ng build command generates 13 initial (i.e. not lazy-loaded) bundles.

I don't know if it's expected or not, but if it is, I don't understand the point of having so many initial bundles (the browser builder used to generate only 3).

Won't that automatically cause performance issues, given that browsers limit the number of concurrent requests to a given domain?

Describe the solution you'd like

Unless there is a good reason (that I'm currently missing) for so many bundles, I'd like the builder to generate fewer bundles: one for the polyfills (that could be cached for a long time) and one for the application for example.

If there is a good reason for so many bundles, please enlighten me :-)

Describe alternatives you've considered

No response

@pumano
Copy link

pumano commented Nov 10, 2023

@jnizet that how esbuild works. If you don't use http/2 it's would be drop in core web vitals. esbuild has open issue evanw/esbuild#207 but that is not implemented.

If you have http/2 setup that amount of chunks not an issue for you

@alan-agius4
Copy link
Collaborator

Indeed this is expected, this is becasue esbuild does cross chunk code motion which webpack does not, which results in a more smaller chunks.

When using HTTP/2 or HTTP/3 the number of files does not have any performance penalty.

@alan-agius4 alan-agius4 closed this as not planned Won't fix, can't repro, duplicate, stale Nov 10, 2023
@jnizet
Copy link
Contributor Author

jnizet commented Nov 10, 2023

OK. Too bad this isn't considered.
Couldn't the CLI, which is able to know which chunks are initial and which chunks are lazy-loaded, concatenate the initial chunks generated by esbuild?

@alan-agius4
Copy link
Collaborator

@jnizet, containing the chunks will actually cause unnecessary code to be downloaded when navigating to a particular page.

Thus, for a performance perspective, having a lot of chunks should be better if using modern HTTP protocols. Although I do see that sometimes esbuild does generate super small chunks under 1 kb, but that is by esbuild design.

Concatenating chunks is definitely not something the Angular CLI should be doing. The bundler has the full context of how chunks should be split to download as code as possible.

@elgerm
Copy link

elgerm commented Nov 28, 2023

@alan-agius4 when you say should be better, is this tested? How does esbuild know which pages where are thus creating chunks per page as webpack builder does?

Our website shows very bad performance after upgrading to angular 17. Lighthouse reports too many chained requests (150 chunks!):
image
before with angular 16 it was only 3 chunks and much better perf:

image

@pumano
Copy link

pumano commented Nov 28, 2023

@elgerm you are right, but check my message above, try to upgrade your http to http/2 or better http/3 to get better performance, because http 1.1 has only 6 parallel connections in browser (that IS reason why concatenation in vendor chunk exist) while http/2 has 100

@alan-agius4
Copy link
Collaborator

alan-agius4 commented Nov 28, 2023

Hi @elgerm,

Which HTTP version are you using? In the case of HTTP 1 it is expected that there is a performance regression. It is also important mentioning that lighthouse should not be ran against a the local dev-server.

Esbuild performs code motion and splits the code based on it's usage which does create more chunks. This is described here: https://github.com/evanw/esbuild/blob/main/docs/architecture.md#code-splitting. Webpack has less chunks as it's unable to perform this sort of advanced optimization.

What is the lighthouse score before and now?

@elgerm
Copy link

elgerm commented Nov 28, 2023

Hi guys, Lighthouse score went down from 70ish to 39.

We're behind cloudflare and have http3 enabled.

I switched back to 16 for now as this was unacceptable performance.

We get about 50k daily unique visitors from SE Asia, 33% use old devices and may find this unusable. Hence the question if it has been tested that so many small chunks don't degrade performance. There must be a penalty from loading and bootstrapping so many small chunks?

Angular went from module based to component based and now switched to esbuild that now generates a chunk per component/directive/pipe. There's 450 chunks in the dist folder 🤯.

Is it possible to go back to webpack build w 17?

@pumano
Copy link

pumano commented Nov 28, 2023

@elgerm its possíble to use webpack with 17 angular. Just use browser target, not browser-esbuild

@sod
Copy link

sod commented Dec 8, 2023

The lighthouse block Avoid chaining critical requests is unfortunate reporting. You can only pick one:

  1. many chunks but less unused code
  2. few chunks but more unused code

The old way (less & bigger chunks) is apparently worse as it's reported under reduce unused javascript with a real penalty.

But 152 files on initial load indeed does sound scary. Sadly esbuild has no minChunkSize setting like webpack does.

@SamanthaAdrichem
Copy link
Contributor

SamanthaAdrichem commented Jan 3, 2024

Our system creates waay more than those 150 chunks, it's actually at 240+ initial chunks (and 472 lazy chunks).

It is a rather large platform.
We went from 58 requests at initial load to over 400

It's still fast though, but I would like the preload to be reduced/delayed.

Tbh, though Angular is made for larger systems. It often feels like the really really large systems like this are not being tested out well. (not trying to bash here, just how it feels, I still love angular and the way it works a lot)

(Though it could just be that we should set things up differently, but there isn't much documentation about building a big system)

Angular 16: (still on webpack)
image

Angular 17: (no longer on webpack)
image

@Martinspire
Copy link

Martinspire commented Jan 23, 2024

Small project but a lot of dependencies that didn't combine their scripts which results in 698 chunks. I'm all for optimizing, but this is just weird. Especially seeing most are external dependencies which could very well be combined (or at least provide the option to do it). It currently serves 304 chunks for the first page of our project (most of it are external dependencies)

@pumano
Copy link

pumano commented Jan 23, 2024

what I really recommend to all as workaround is to use webpack for production and serve app for development with --forceEsbuild=true it run your project faster for development purposes, but production code build as previously using webpack. Some problems can be potentially found with background-image resolving difference, but in other cases all works same. We use it in our large app until esbuild not provide some solution for it.

@joewIST
Copy link

joewIST commented Feb 16, 2024

This is also an issue for us. Looks like we may need to revert to using webpack for production as well, as the chaining of chunks on initial load is causing non-trivial performance issues.

@born2net
Copy link

born2net commented Mar 2, 2024

same here, our server is http (not http/2) and so we need a way to keep total files same as webpack. Any updates? solutions?

@sod
Copy link

sod commented Mar 2, 2024

Any updates? solutions?

First step is look what is in those small chunks. Lets say it's lots of lodash-es code, because lodash is 200 little functions. You could use commonjs lodash instead. esbuild will still delete unused code, but won't rip it apart anymore, as it can't guarantee side effect freeness.

If your 500 byte chunks contain simple strings, you could move those strings behind a barrel.

e.g. if you have constants like here:

// constants-a.ts
export const foo = 'foo string';
export const bar = 'bar string';

// constants-b.ts
export const lorem = 'lorem string';
export const ipsum = 'ipsum string';

you create a barrel like:

// constants.ts
export * from './constants-a.ts';
export * from './constants-b.ts';

And if you import {lorem} from 'constants' it won't put const lorem = 'lorem string'; into a chunk, as esbuild doesn't split chunks for barrels (at least right now, could of course change)

So there are some ways to bend esbuild to your will.

Third option might be, to contribute to esbuild something like a minChunkSize option like webpack has.

@SamanthaAdrichem
Copy link
Contributor

Honestly I commented because the amount of chunks can be insane, and imho should be improved with better bundling/lazy loading.

but it is faster for us and working fine on chromebooks even. Cloudflare is caching it for free (and if you want you could even upload it to cloudflare workers)

Probably an Unpopular opinion, solutions because people are still on http1 should imho not happen. Use webpack then or upgrade your servers. Http 2 is from may 2015 and almost 9 years old now.

@LuisTovar0
Copy link

this issue is causing my colleagues to call me "the chunk man"

@Martinspire
Copy link

Honestly I commented because the amount of chunks can be insane, and imho should be improved with better bundling/lazy loading.

This is the main reason yes. I would already be happy if I could force every module to be a chunk, so that with lazy routes it at least only needs to do one request. But its difficult to probably see what belongs where. For standalone stuff it will likely be even more difficult and the only one to tell esbuild to do better, is angular itself. Since it could figure out a dependency tree that can be used to optimize it. Perhaps using routes to bundle them or something.

Probably an Unpopular opinion, solutions because people are still on http1 should imho not happen. Use webpack then or upgrade your servers. Http 2 is from may 2015 and almost 9 years old now.

You assume that every company owns their own servers or that their security/monitoring/etc tools can handle http2 already. Lets be real here, some bad admins can't be bothered or simply don't want to do anything about it. So having the option is always going to be of some benefit to people.

Third option might be, to contribute to esbuild something like a minChunkSize option like webpack has.

That or a dependency tree that can be used to figure out what is connected and can be bundled. With hot modules replacement / reloading, you already need to know what stuff needs to be reloaded with the changes you've made.

@mihailik
Copy link

mihailik commented Mar 20, 2024

How can we post-process this exponential explosion of files?

Shipping ESBuild mode in Angular without built-in handling of chunks makes it highly disruptive.

Apart from complicating debugging/diagnostics in browser, it rakes up deployment and support risks couple orders of magnitude.

  • Network errors during deployment are way more probable, being 100x more transactional updates to the filesystem.
  • It becomes impossible to spot partial/incomplete deployment.
  • Every and all DevOps processes that were tuned to small number of files will grind nearly to halt

Anecdotal case:

  • we used to have Angular15 deployment taking 20-30 minutes using rsync to transfer dist/ files
  • by using webpack.optimize.MinChunkSizePlugin we lowered it to 3-4 minutes
  • now we're risking going back to excessively long deployments, even way worse

If Angular team is not ready to improve the situation at this point, can you folks please give us direction and help adding customisations/plugin/script to implement chunk coalescing at the project side?

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Apr 20, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests