Created by: gaearon
Curiously, out of the box Webpack doesn’t handle long term caching very well. Granted, it’s not a very simple problem for a bunch of reasons:
- Both JS and CSS are first-class modules and can be put in chunks
- Webpack has code splitting API so chunks need to know about each other
- ... but if they know each other’s filenames, their hashes depend on one another!
This problem is described here, and if you have nothing better to do with your evening, you can read through this issue: https://github.com/webpack/webpack/issues/1315. (I did.)
This PR makes a bunch of improvements to longer term caching of the apps we generate. I used different solutions linked from https://github.com/webpack/webpack/issues/1315 as inspiration. The result config is a little bit intimidating but I believe it provides a sufficiently better experience for production apps. This solves a few problems:
- Changes to CSS shouldn’t invalidate JS.
- Updating React shouldn’t invalidate app code.
- Changes to async bundles shouldn’t invalidate the root bundle.
Test Plan
I will provide a comparison of how often we bust caches below and after this change. I got it by repeating the same operations over time and comparing requests in Network tab.
Before
main.455a59df.js
main.9a0fe4f1.css
logo.84287d09.svg
### change CSS
main.6f795e0f.js [!] bad: updating CSS shouldn't bump JS
main.d57c128d.css [*]
logo.84287d09.svg
### change CSS again
main.f0bce2a4.js [!] bad: updating CSS shouldn't bump JS
main.d42a29cb.css [*]
logo.84287d09.svg
### change JS
main.8f645d9d.js [*]
main.d42a29cb.css
logo.84287d09.svg
### change CSS back
main.8301d559.js [!] bad: updating CSS shouldn't bump JS
main.9a0fe4f1.css [*]
logo.84287d09.svg
### add async JS bundle
main.a34625e7.js [*]
main.65027555.css [*]
1.67fca831.chunk.js [*]
logo.84287d09.svg
### change async JS bundle
main.3a030413.js [!] bad: changing async shouldn't bump root
main.65027555.css
1.df221dec.chunk.js [*]
logo.84287d09.svg
### change JS
main.ce52003f.js [*]
main.65027555.css
1.df221dec.chunk.js
logo.84287d09.svg
### update React
main.733b24be.js [!] bad: updating React shouldn't bump root
main.65027555.css
1.a9207a9f.chunk.js [!] bad: updating React shouldn't bump split
logo.84287d09.svg
After
vendor.ad62e36d.js
main.a83bc981.js
main.9a0fe4f1.css
logo.84287d09.svg
### change CSS
vendor.ad62e36d.js
main.a83bc981.js
main.d57c128d.css [*]
logo.84287d09.svg
### change CSS again
vendor.ad62e36d.js
main.a83bc981.js
main.d42a29cb.css [*]
logo.84287d09.svg
### change JS
vendor.ad62e36d.js
main.6a8181e8.js [*]
main.d42a29cb.css
logo.84287d09.svg
### change CSS back
vendor.ad62e36d.js
main.6a8181e8.js
main.9a0fe4f1.css [*]
logo.84287d09.svg
### add async JS bundle
vendor.ad62e36d.js
main.8945b1c6.js [*]
main.65027555.css [*]
0.dc08343c.chunk.js [*]
logo.84287d09.svg
### change async JS bundle
vendor.ad62e36d.js
main.8945b1c6.js
main.65027555.css
0.f9c57019.chunk.js [*]
logo.84287d09.svg
### change JS
vendor.ad62e36d.js
main.a29f4c4f.js [*]
main.65027555.css
0.f9c57019.chunk.js
logo.84287d09.svg
### update React
vendor.425b0082.js [*]
main.a29f4c4f.js
main.65027555.css
0.f9c57019.chunk.js
logo.84287d09.svg
Revert Plan
I don’t see why we would have issues with this especially considering that user can’t configure anything (and thus potentially break something). Worst case, we remove these plugins / options because they are strictly additive and won’t be a breaking change.
This complicates the post-eject configuration but I think it’s not a big issue especially considering this feature is something people actively want when configuring webpack. There is a reason https://github.com/webpack/webpack/issues/1315 has 120 comments.
Future Plans
We can add more stuff to vendor
bundle in a smart way. For example we could whitelist certain projects that don’t change too often explicitly, of we could apply some heuristic (e.g. most people would likely want any polyfills and stuff like React Router to end up there). This can be discussed separately.
Alternatives
We could point people to React CDN builds. Unfortunately this doesn’t solve issues with CSS invalidating JS. It also doesn’t currently play well with npm addons. And it won’t fix the issues with code splitting (if people ever get to that, which they do in real apps). So I feel like, until Webpack provides something better out of the box, this is a sensible solution.