facebook/create-react-app

An explanation of my magically shrinking build size with subsequent alpha builds

wildpow opened this issue ยท 10 comments

I am a new web dev and I would love an explanation or resources explaining my magically shrinking build folder size with subsequent alpha releases of create-react-app. I have spent a lot of time and energy making my current client-side React as fast as possible through HTTP/2 push, dynamic imports, webP images, optimizing critical rendering paths, just to name a few and for the most part with some success. My app's LightHouse score went from the 70's to the high 80's but, now with the latest alpha I'm making me rethink my optimizations due to my LightHouse scores tanking.

next.66cc7a90:
screen shot 2018-06-18 at 12 17 38 am
vs
next.3e165448:
screen shot 2018-06-18 at 12 16 18 am

From 1.1.4 to 66cc7a90 I found most of my savings came from smaller source maps but, 3e165448 is some next level stuff.

React-scripts version build folder size
1.1.4 12.6 megs.
2.0.0-next.66cc7a90 8.3 megs.
2.0.0-next.3e165448 4.6 megs.

The main difference between 3e16544 and 66cc7a9 is webpack 3 -> 4 upgrade.

I got rid of all my route splitting and my app's still slower with this update.
screen shot 2018-06-18 at 1 02 22 am

Forgot to mention that source-map-explorer can be used to checkout the difference in bundle size. The catch is that with webpack 4 there are at least 2 different chunks (main/vendor) and you'll have to input them one by one.

Timer commented

Related: #4631

That's really odd -- we turned on module concatenation which should've made evaluation faster not slower. This is really worry-some.

I wonder if @TheLarkInn knows of anything off the top of his head why webpack 4 would make bundle evaluation slower.

Timer commented

@wildpow can you please share your project? It would be fantastic in helping get to the bottom of this.

edit: to be clear what you are doing is most likely correct, it's probably a problem with us that needs resolved.
The magically shrinking bundles are coming from newer versions of the bundler which is becoming more efficient & is capable of not bundling source code from dependencies which is unused.

Yeah I don't think we can really say anything conclusive without seeing a reproducing case.

Timer commented

Working theory (with make-up numbers):

When route splitting in app before vendoring, you would end up with 1 main bundle and 2 chunks, each around ~1.1MB if you had large sets of dependencies and app code.

Since we added vendoring, you now will end up with a vendor bundle at ~3MB, and 3 chunks around 50-100kb.
This is obviously sub-optimal because you have to evaluate 3MB to mount your app instead of the previous 1.1MB.

See this theory materialized in #4631 (comment).

We need to generate a vendor bundle per chunk imo, otherwise we need to disable vendoring.

Also as a heuristic, I don't think we should turn on vendor bundles unless there's a single import() in the source code to satisfy cases like #4632.

What do you think?

@Timer
Currently test base-line build in production. Master branch of the repo is still on react-scripts 1.1.4

Base-line:
Deploy react-scripts@2.0.0-next.66cc7a90
Branch react-scripts@2.0.0-next.66cc7a90

With code splitting:
Deploy react-scripts@2.0.0-next.3e165448
Branch react-scripts@2.0.0-next.3e165448

Without code splitting:
Deploy 3e165448-no-splitting
Branch3e165448-no-splitting

Timer commented

Excellent @wildpow, thank you! I hope to have some free time soon to get to the bottom of this. If anyone can look at is sooner, please do!

This has already been covered but I just want to state that I am observing the exact same issue as #4631 where before code splitting on routes would break up my vendor chunks into ~300kb pieces, with 2.0 my vendor is huge and my app code is ~30kb. Code splitting reduces my app code to ~10kb chunks and increases my already big vendor chunk by ~20kb, so it is actually hurting performance.

I think the vendor splitting can be useful but there needs to be some way to split out vendor chunks or perhaps keep the 1.x behavior.