Vite: How It Achieves Constant Time Builds

Published on: Sat Sep 10 2022

Series

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

build time vs codebase size comparison between bundling tools
Buld time vs Codebase size

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.

Vite: on-demand vs all at once bundling
Vite: prebundling dependencies and caching in filesystem
Vite: Native ESM Syntax
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.

Comparison of the typical to ideal bundling process
Comparison of typical to ideal bundling process
Vite: Serving modules on demand
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 )

Vite: prebundling
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!)


Enjoy the content ?

Then consider signing up to get notified when new content arrives!

Jerry Chang 2022. All rights reserved.