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

Code splitting is creating many small unnecessary chunks #3780

Open
ChristopherPHolder opened this issue May 25, 2024 · 1 comment
Open

Code splitting is creating many small unnecessary chunks #3780

ChristopherPHolder opened this issue May 25, 2024 · 1 comment

Comments

@ChristopherPHolder
Copy link

ChristopherPHolder commented May 25, 2024

When code splitting, esbuild does not seem to take into account code that is already downloaded or cannot be loaded on its own.

It seems important to distinguish between an application real entry points and its sub-entry points, as this has a direct impact on loading performance.

Lets say an application can only be accessed via main.ts, so it only has real "entry point", and then it has a bunch of lazy features "sub-entry points".

If the piece of shared code is consumed by main.ts, when the lazy feature imports it, the code is already there and it will be cached in some way. But in the case where there are many lazy features and many pieces of shared code that are consumed by main.ts it will split this into many little chunks.

Is there a benefit in splitting code that will necessary be imported in main.ts which will then also be imported in one of its lazy features?

These chunks seem unnecessary and are causing performance issues as to many chunks will increase load time can have a direct impact on user experience on Core Web Vitals.

Is there a way to reduce these "unnecessary chunks"?

Ideally i would like that when building esbuild looks at the node tree graph and recognizes when the code should have already been loaded by a parent node and does not splitting into smaller chunk.

Is there any way currently to reduce the number of chunks? Potentially with a plugin?

I have notice that by using bundles strategically i can reduce the number of chunks but this is not an ideal approach and it seems i am partially opting out of tree shaking.

I have tested this approach on a minimal angular application.

And created a minimal reproducible example using only esbuild, and typescript.

Minimal Preproduction

https://github.com/ChristopherPHolder/esbuild-code-splitting

The example is a minimal node cli with no dependencies only meant to illustrate this issue.

The CLI contains an execution file from where its meant to be accessed app.js. In theory it should not be accessed from anywhere else.

It constrains 3 file which are ui{1,2,3}.js and are statically imported both in app.js and in feature{1,2,3}.js which are in turn dynamically imported in app.js and nowhere else.

Even tho, all ui{1,2,3}.js are statically imported in app.js which is the only real declared entry point, it will create a chunk for each of these 3 files.

If we on add an index.js from where all ui{1,2,3}.js are imported then it will still chunk them as a separate file but it will only create 1 additional chunk.


I am aware that this is currently expected behaviour as the docs states it pretty clearly:

Note that the target of each dynamic import() expression is considered an additional entry point.

However, i am looking for a solution to reduce the number of chunks, as this seems to be a large performance issue at the moment.

@sod
Copy link
Contributor

sod commented May 28, 2024

The pink chunks are what we talking about:

CleanShot 2024-05-28 at 12 31 23@2x

The general chunk structure is pretty nice. But the more import('...') entries you create, the more of these pink <2kb chunks appear

For us these micro chunks are created by:

  1. Redux actions that are used at 2+ places. These chunks look like
import{Mb as e}from"./chunk-OMCORABT.js";var t=e("[Product] Clicked");export{t as a};
  1. Or assets from { loader: { ".webp": "file" } that are used at 2+ places. These chunks look like:
var l="./media/image-FZ7VCKMX.webp";export{l as a};

Would be awesome if esbuild could inline the file loader string or via a minChunkSize pull into the largest parent chunk.

Gzip and less http requests would make this more efficient. And you reduce the chance of running into the maximum allowed requests limit.

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

No branches or pull requests

2 participants