Content
Introduction
If you used webpack or rollup on large projects then you probably know the struggle of having the long build times between each change.
This was one of the pain points that the Evan You thought about addressing when he created Vite.
He took a look at the existing solutions, and thought of ways to iterate on the design.
These new ideas were made possible because of:
Better tooling (
esbuild
,swc
)Native ESM support (on browsers)
with these new changes, he had a vision of a new tool (Vite) which achieves:
Faster build times - Keeping build times constant that scales with codebase size
Better Developer Experience (DX) - Building a tool from the ground up to improve DX — faster feedback cycle, overall experience, and extensibility
A more integrated Server-side rendering experience - Providing a more integrated experience, tooling for server-side rendering, and solving pain points like having multiple configuration files
From a technical standpoint, there were 3 key design decisions that made it possible for Vite to achieve these optimization goals.
Let’s go through them one by one.
3 Key design decisions
When webpack was introduced, it was the best tool at that time.
Then rollup came on the scene, it was a new bundler with a focus on simplicity.
Both of these tools took a very similar approach to bundling — which is, when the files changed, it rebuilt the whole bundle.
This means that as your codebase grows, the build times would grow linearly with this increase.
This leads to question of why can’t we just rebuild the files that has changed (and not the whole bundle) ?
And that’s the exact approach that Evan took when designing Vite.
All of these decisions were made possible because modern browsers now support native ESM.
Let’s go through these 3 key design decisions.
1. Use of Native ESM
The first decision makes all the other decisions possible, and that was the decision to use native ESM.
In doing so, Vite can now delegate handling of the module system to the browser, and just handle the processing (transpilation, transforming etc) of the requested modules.
With this small change, it actually allows Vite to also rethink how recompilation works in the development environment.
2. Rethinking Recompilation
With the use of native ESM, Evan can now rethink how the build lifecycle works.
One of those ideas is to actually separate the source code and dependencies.
This was actually one of the main bottleneck in bundlers like webpack and rollup.
Separating the two, allows Evan to re-design the builds to better suit the lifecycle of each of these use cases independently.
The use case are:
Source code - Changes frequently
Dependencies - Changes less frequently
When changes occur, instead of rebuilding the whole bundle each time, Vite will just serve the modules on-demand.
This is done by leveraging the Vite middleware, and the native ESM on browsers which leads to better overall performance.
Now, in this section, we only talked about the recompilation.
What about the dependencies ? How does Vite handle that part ?
That’s where prebundling comes in.
3. Pre-bundling
To further optimize the build process, Vite will prebundle the dependencies in your project.
It does this by crawling the source code, and figuring out which dependencies needed to be prebundled, then run them through esbuild.
The outputs are cached in the filesystem based the lockfiles, and they will be invalidated as needed.
So, that means no more rebuilding on every change!
Other than prebundling dependencies, Vite also performs the following optimization in the process:
Conversion to Native ESM - Vite will convert the modules using CommonJS or UMD into native ESM
Optimizing performance - Vite will concat modules to prevent waterfall requests when using native ESM (ie
lodash-es
)
Conclusion
So, to recap, there are 3 key decisions made by Evan You (Creator of Vite) that allows for the overall performance improvements in the build times as your codebase size increases.
They are:
Use of Native ESM - Module systems are now natively supported on browsers, going from building everything at once to serving bundles on-demand
Rethinking Recompilation - Separating the source code and dependencies, and optimizing the build for these two sources independently
Pre-bundling - Dependencies are pre-bundled using esbuild then cached on the filesystem, the dependencies are only invalidated whenever the lockfile changes
And that its! I hope you learned something new!
If you found this helpful or learned something new, please share this article with a friend or co-worker 🙏❤️! (Thanks!)