Practical problems
Before browsers supported the ES module, JavaScript did not provide a native mechanism for developers to develop in a modular manner. This is why we are familiar with the concept of “packaging” : using tools to grab, process, and concatenate our source code modules into files that can be run in a browser.
Over time, we’ve seen tools like WebPack, Rollup, and Parcel evolve to dramatically improve the development experience for front-end developers.
However, as we start building larger and larger applications, the amount of JavaScript code we need to process grows exponentially. Large projects with thousands of modules are quite common. We started hitting performance bottlenecks — tools developed in JavaScript often took a long time (even minutes!). To start the development server, even with HMR, it takes a few seconds for the effect of the file changes to be reflected in the browser. Over and over again, slow feedback can have a huge impact on a developer’s productivity and happiness.
Vite aims to take advantage of new developments in the ecosystem: browsers are starting to support ES modules natively, and JavaScript tools are increasingly written in compiled languages.
Slow server startup
When cold starting the development server, the packager-based startup must first crawl and build your entire application before it can be served.
Vite improves development server startup times by initially separating application modules into dependent and source modules.
- The dependencies are mostly pure JavaScript that does not change at development time. Some large dependencies, such as component libraries with hundreds of modules, are also expensive to handle. Dependencies also often exist in multiple modular formats (such as ESM or CommonJS). Vite will pre-build dependencies using esBuild. Esbuild is written using Go and is 10-100 times faster than a prepacker-built dependency written in JavaScript.
- The source code usually contains files that are not directly JavaScript that need to be transformed (such as JSX, CSS, or Vue/Svelte components) and often edited. Also, not all source code needs to be loaded at the same time (such as code modules based on route splitting). Vite provides the source code in native ESM mode. This essentially lets the browser take over part of the packaging: Vite simply transforms the source code when the browser requests it and makes it available on demand. Code is dynamically imported according to the situation, which is only processed if it is actually used on the current screen.
\
\
Slow update
Rebuilding the entire package is inefficient when the packager-based startup is in place. The reason for this is obvious: the speed of updates plummets as the size of the application increases.
Some of the packer’s development servers store the build content in memory, so they only need to inactivate a portion of the module diagram when the file changes [1], but it still needs to rebuild the entire page and reload the page. This is expensive, and reloading the page eliminates the current state of the application, so the packer supports dynamic module hot reload (HMR) : allowing a module to “hot replace” itself without affecting the rest of the page. This greatly improves the development experience — however, in practice we have found that even with the HMR model, the rate of hot updates decreases significantly as the application scale increases.
In Vite, HMR is executed on native ESM. When editing a file, Vite only needs to precisely inactivate the chain [1] between the edited module and its nearest HMR boundary (most of the time just the module itself), allowing HMR to keep up to date quickly regardless of the size of the application.
Vite also uses HTTP headers to speed up whole page reloads (again letting the browser do more for us) : requests from source modules are negotiated in the Cache according to 304 Not Modified, while dependent module requests go through cache-control: Max-age =31536000,immutable Strong cache, so once cached they do not need to be requested again.
Once you’ve experienced the speed of Vite, it’s a big question mark whether you’ll be willing to put up with packer development the way you used to.
Why do production environments still need to be packaged
Although native ESMs are now widely supported, it is still inefficient to publish unpackaged ESMs in a production environment (even with HTTP/2) because of the additional network round-trips that nested imports cause. For the best loading performance in a production environment, it is best to tree-shaking, lazy loading, and chunk splitting your code (for better caching).
Ensuring optimal output and consistent behavior between development servers and production builds is not easy. So Vite comes with a set of buildoptimized build commands, right out of the box.
Why not pack with ESBuild?
While EsBuild is astonishingly fast and already a great tool for building libraries, some important features for building applications are still under development — particularly code splitting and CSS handling. For now, Rollup is more mature and flexible when it comes to application packaging. However, we do not rule out using EsBuild as a production builder in the future when these features are stable.