Front-end Build Tools in 2023
When we say a front-end build tool, we mean:
- It is able to build production assets.
- It has a dev server with HMR.
- It supports common technology like Sass, LESS, CSS modules, code splitting.
- It supports plugins.
- It does NOT restrict UI framework to use or bundle unnecessary application runtime, like Redux.
Ready for production
When we say a build tool is ready for production, we mean:
- It is popular. (>100k weekly downloads from npm)
- It is actively maintained. (at least 1 release in last 6 month)
- It has no critical performance or compatibility issues.
Vite 4 is based on esbuild (written in Go) and Rollup. It adopts un-bundled dev server for faster HMR and bundled dependencies for faster cold booting.
Vite 4 has a new swc based React plugin @vitejs/plugin-react-swc to replace the old babel based plugin. This makes React Fast Refresh 20x faster than before. It is still one of the fastest solutions.
Vite is easy to use. You can start without any configuration. When need customization, the user document is detailed and helpful. Writing plugins, or even create your own build tools based on Vite, is relatively easy. (Thanks to Vite's JS API design)
Vite has a large community and eco-system, making it easier to find plugins for your needs and solutions for your questions.
Vite 4 isn't perfect:
- In my own tests, Vite 4's pre-bundling is much slower than Vite 2, and not comparable to esbuild. See #8850. Luckily, this only affect cold start of dev server, and Vite team is working on a fix.
Parcel 2 is based on a collection of efficient tools written in Rust, like swc, Parcel CSS, to achieve better performance than Webpack 5. Parcel 2, which default configuration, has overall performance in the middle of Vite 4 and Webpack 5. But you can increase performance by enable some experimental features, like swc minification.
If HMR and build performance is your main concern (code base with millions of lines of source code), Parcel 2 is the best choice for you.
Parcel 2 isn't perfect:
- For React (still CJS only in 2023!) projects, you need to polyfill node built-in modules, like process. In theory, parcel will install these polyfill packages automatically when needed. However, it may not work when you use pnpm rather than npm.
- Plugin eco-system is still small.
- JS API is not as complete as Vite. When you create your own build tools, Vite is probably a better choice than Parcel as a basement.
Webpack is still the most popular choice, with the most complete features and largest eco-system.
Webpack is slow, but you can make it faster by using plugins and loaders:
Some exciting & promising projects in early stage.
A webpack alternative written in Rust. Created by ByteDance.
High performance bundler for Next.js, support SSR. Unqualified because it only works in Next.js framework (for now).
Build tool from Ant Group. Unqualified because it is heavily customized, only supports React, and bundled too many runtime, like Redux.
Build tool from Alibaba. Unqualified because it is heavily customized, only supports React, and bundled too many runtime, like Redux.
Create React App
React's official build tool. Unqualified because it only supports React and doesn't support configuration and plugins.
|dev server cold start||dev server warm start||production build|
More details, see https://github.com/guoyunhe/front-end-build
Technology that can potentially make build tools better and faster in future.
Node.js Module Format: Native vs WASM vs JS
Here are more and more Node.js modules written in native language, like Rust and Go. However, written in native languages doesn't mean native performance. It depends on the type of output modules: native, WASM or JS. Without threading, performance from highest to lowest is native > WASM > JS. JS and WASM are single threaded by default, unless you use cluster mode (I haven't see successful story in front-end builders). Native modules, like esbuild, can make full use of multi-threading to achieve even better performance.
However, native modules must be compiled individually for each platform, and it doesn't run in browsers. On the contrary, JS and WASM are "compile once, run everywhere", which is much easier for distribution.
In practice, native modules are mainly used in core components with heavy loads, like rspack, swc, esbuild. Plugins etc. are still written in JS. WASM is not as popular as the other two. One use case is swc plugin development.
Faster Sass compiler
Sass' old node-sass compiler was deprecated. The new official Dart Sass compiler is confusing:
- The native build has no way to be integrated into existing Node.js tool chains, unless you use Node.js' exec function (not recommended in production usage).
This is a deal breaker for production projects that heavily use Sass. Many projects choose to stick with deprecated node-sass compiler.
Here are new Sass compilers written in Rust:
- grass. It only released as WASM, not native module, which doesn't have much advantage over Dart Sass (JS).
- rsass. Unfortunately, it doesn't support Node.js API, which makes it difficult to integrate with existing front-end eco-system.
Faster TypeScript checker
TypeScript is slow cause it is written entirely in JS. Luckily, we usually don't need to check types when building a front-end project. Babel, esbuild or swc will simply ignore all types. However, if your project has strict QA and you have to run tsc, your CI workflow will be slow down even if your project is small (10k lines) or medium size (100k lines).
Re-implementation of TypeScript has become a popular idea. We have seen some attempts:
- stc by the author of swc. You can already try it by yourself.
- deno, an alternative of node.js, has a plan to natively support TypeScript.