DEV Community

Cover image for Vite: How It Achieves Constant Time Builds
Jerry
Jerry

Posted on • Originally published at jerrychang.ca

Vite: How It Achieves Constant Time Builds

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

Image build time vs codebase size comparison between bundling tools

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.

Image Vite: on-demand vs all at once bundling

Image Example using native ESM syntax

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.

Image Comparison of the typical to ideal bundling process

Image Vite: An illustration of the process of serving modules on demand

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)

Image Vite: prebundling dependencies and caching in filesystem

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!)

Top comments (2)

Collapse
 
therakeshpurohit profile image
Rakesh Purohit

I worked a lot around bundling and transpiling that too in-browser and I can say that the way you have explained is superb!!

Collapse
 
jareechang profile image
Jerry

Thank you @therakeshpurohit , I appreciate the feedback 🙏