Vite is faster than Webpack

CNHur HyeonBin (Max)
Reponses  03months ago( Both Korean and English Version )

1. Introduction

Currently, many people learning React use tools like Create React App (CRA) or Vite to easily create projects and develop Single Page Application (SPA) based applications. While these tools allow you to quickly start React projects, React internally has very complex configurations and structures, and directly configuring these settings with JavaScript is more complex and difficult than you might think.

CRA and Vite serve the role of automating these complex environment configurations. CRA is a tool released by Facebook in 2016 that automatically generates the basic structure and configuration for React projects. However, recently it has been officially announced that it will no longer be updated and will maintain its current state. On the other hand, Vite is a build tool that appeared in 2020, gaining popularity by highlighting fast development speed as its strength.

Why are these two tools always compared?

CRA and Vite are frequently compared because their transpilers and bundlers, which are the most core internal structures for running React in browsers, are different, resulting in very different purposes and performance. To understand what transpilers and bundlers are, we first need to briefly look at how React projects work.
We usually write JSX or TSX files when creating project components and configure these components to run in browsers. However, this process is not simply about writing and executing code. To make React work properly in browsers, two key components are needed: transpilers and bundlers. How these two elements work and how CRA and Vite handle them differently affects the development experience and performance.

Transpiling

Can code written in JSX or TSX be executed directly in browsers? No, it cannot be executed directly. Since browsers basically don't understand JSX or TSX syntax, it must be converted to JavaScript that browsers can understand, and this process is called transpiling.

Bundling

Bundling refers to the process of combining multiple JS files into one so that browsers can receive them all at once. However, if JSX or TSX has been converted to JavaScript through a transpiler, this code can be executed directly in browsers. Then, why is bundling necessary? The answer to this question is related to the characteristics of SPA (Single Page Application), which React mainly uses, and the HTTP protocol. SPA refers to an application configured to switch between multiple screens within one page, allowing various content to be displayed without page refresh. To do this, it's common to download all necessary JavaScript files in advance when users first load the app. This is because it enables smooth operation during page transitions.

HTTP is basically a TCP-based protocol, and certain connection/disconnection costs occur each time data is exchanged. It's not simply that smaller files are faster; when requests increase, communication overhead increases accordingly.

For example, suppose you need to send 3 messages to a friend.
And suppose it takes the following time each time you send a message:

  • Basic transmission time: 1 second

  • Transmission time based on message length: Varies according to message content length

If you send messages one by one separately,

1st message: Basic 1 second + length-based time 1 second = 2 seconds

2nd message: Basic 1 second + length-based time 1 second = 2 seconds

3rd message: Basic 1 second + length-based time 1 second = 2 seconds

Total 6 seconds

But if you bundle and send all 3 messages at once

Basic 1 second + length-based time 3 seconds = Total 4 seconds

In other words, even small data can be slower when sent in multiple parts, and this is the core reason for bundling.

The same applies to JavaScript files. When transmitted in multiple parts, overhead occurs with each request, reducing network efficiency. Therefore, bundlers combine multiple JS files into one so browsers can receive them all at once. This can provide benefits such as improved load speed, initial rendering optimization, and traffic reduction.

2. Differences in Internal Structure and Operating Methods

CRA and Vite use different tools to run React projects, and the procedures in Dev Mode and Production Mode are also different.

As shown in the diagram, CRA uses babel and Webpack for transpiling and bundling, while Vite uses esbuild and rollup for transpiling and bundling. Additionally, CRA and Vite show differences in the procedures for rendering projects to browsers in both Dev Mode and Production Mode.

CRA Dev Mode, Production Mode Steps.pngVite Dev Mode, Production Mode Steps.png

Among these, esbuild is a high-performance build tool written in Go language, which is much faster than Babel in terms of speed. This gives Vite a significant advantage in build performance, and also, Vite skips bundling in development mode. Instead, it processes code in module units and uses a method of loading only necessary modules when needed (HMR-based, HMR explanation is introduced below). This relates to the necessity of bundling explained earlier and the reason why complete bundling is not necessary in development environments.

As a result, because Vite skips bundling in development mode, it provides much faster execution speed than CRA, and also, due to performance differences in the transpilers themselves (esbuild vs Babel), not only are there differences in processing steps, but there are also noticeable performance differences in initial loading time and HMR response speed.

 

3. esbuild vs Babel

esbuild and Babel show distinct differences in transpiling performance because their development purposes and internal structures are different from the start. The reason why CRA and Vite chose different transpilers also stems from differences in tool design philosophy and performance orientation.

Babel

Babel is a relatively early transpiler, developed in JavaScript and structured based on CommonJS. While it has advantages of flexible structure and a broad ecosystem, due to JS's inherent limitations like single-thread processing and runtime performance limits, its transpiling speed is relatively slow.

esbuild

esbuild is a high-performance transpiler written in Go that appeared relatively recently. Based on Go's high-speed compilation characteristics and multi-thread parallel processing capabilities, it boasts much faster build speeds than Babel. Because it appeared relatively recently, while it has performance advantages, it has the disadvantage that its ecosystem is not as broad as Babel's.

 

4. Webpack vs Rollup

 

4.1. Differences in Internal Structure and Development Philosophy

Webpack and Rollup show fundamental philosophical differences in how they handle bundling tasks. Webpack provides granular control through a role-separated configuration system, while Rollup integrates all functions into a single plugin-centered system.

Webpack's 3-Stage Separation System

Loaders: File Transformation Engine

module: {
  rules: [
    {
      test: /\.tsx?$/,
      use: 'ts-loader',
      exclude: /node_modules/
    }
  ]
}

 

Loaders work in a pipeline manner. When they detect specific file patterns, they transform those files into a form that JavaScript can understand. They handle tasks like converting TypeScript to JavaScript, SCSS to CSS, and images to Base64.

Vendor Splitting: Caching Optimization Strategy

optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendor',
        chunks: 'all'
      }
    }
  }
}

 

Vendor splitting aims to minimize cache invalidation. It separates external libraries that don't change frequently (React, Lodash, etc.) into separate chunks, so library caches remain even when application code changes.

Plugins: Build Process Extension

plugins: [
  new HtmlWebpackPlugin({
    template: './src/index.html'
  }),
  new MiniCssExtractPlugin({
    filename: '[name].[contenthash].css'
  })
]

Plugins intervene at specific points in the build lifecycle to perform additional tasks. They handle all auxiliary functions not covered by loaders, such as HTML file generation, CSS extraction, and bundle analysis.

Actually, when you create a basic React project with CRA and check webpack.config.js with the npm run eject command, you can see this 3-stage structure clearly separated. Looking at CRA's webpack.config.js, while there's a lot of code, you can confirm that loader, optimization, and plugin are clearly separated within the exported function. This structure shows clear separation of concerns and clearly demonstrates that each configuration area has independent responsibilities. File transformation is handled by loaders, bundle optimization by optimization, and build process extension by plugins.

This allows developers to granularly control complex build processes step by step, and when modifying or extending specific functions, they can focus intensively on the relevant area only.

Rollup's Plugin-Centered Architecture

Unlike CRA's Webpack, Rollup integrates all functions into plugins, handling everything plugin-centrically at once.

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    // File transformation (Webpack's loader role)
    typescript(),
    babel({
      babelHelpers: 'bundled'
    }),
    
    // External dependency handling (Webpack's vendor role)
    nodeResolve({
      browser: true
    }),
    commonjs(),
    
    // Build optimization (Webpack's plugin role)
    terser(),
   
    serve({
      contentBase: 'dist',
      port: 3000
    })
  ]
}

Webpack's advantages:

  • Granular control: Independent configuration possible for each stage, suitable for complex requirements
  • Extensibility: Modularized structure advantageous for configuring complex build pipelines

Rollup's advantages:

  • Simplicity: Unified plugin interface minimizes learning curve and configuration complexity

Ultimately, the difference in design philosophy where Webpack targets complex applications while Rollup targets libraries or simple apps creates these architectural differences. Webpack's 3-stage separation system is for the granular control required in large-scale projects, while Rollup's plugin-centered structure reflects a philosophy pursuing efficiency and simplicity.

 

4.2. Differences in Module Systems of Bundling Results

Both Webpack and Rollup are 'bundling tools' that bundle source code into JavaScript files. However, many module systems exist in JavaScript, and Webpack bundles source code based on Runtime Module System, while Rollup bundles based on ES6 module system.

In other words, even with the same source code, the results of bundling with Webpack and bundling with Rollup are different.

Simple source code exists, and we compared the results after bundling with webpack and rollup.

//math.js
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }
export function unused() { return 'unused'; }

// main.js
import { add } from './math.js';
console.log(add(2, 3));

webpack result (you can just skim through this with your eyes):

/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./math.js":
/*!*****************!*\
  !*** ./math.js ***!
  \*****************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
  "add": () => (/* binding */ add),
  "multiply": () => (/* binding */ multiply),
  "unused": () => (/* binding */ unused)
});

function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
function unused() { return 'unused'; }

/***/ }),

/***/ "./main.js":
/*!*****************!*\
  !*** ./main.js ***!
  \*****************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
var _math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math.js */ "./math.js");

console.log((0,_math_js__WEBPACK_IMPORTED_MODULE_0__.add)(2, 3));

/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// Runtime code (module loader system)
/******/ 	var __webpack_require__ = {};
/******/ 	
/******/ 	__webpack_require__.d = (exports, definition) => {
/******/ 		for(var key in definition) {
/******/ 			if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 				Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 			}
/******/ 		}
/******/ 	};
/******/ 	
/******/ 	__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	
/******/ 	__webpack_require__.r = (exports) => {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/ 	
/******/ 	// Module execution start
/******/ 	__webpack_require__("./main.js");
/******/ })();

 

rollup result:

function add(a, b) { return a + b; }
console.log(add(2, 3));

 

As mentioned earlier, because Webpack and Rollup have different module systems and development philosophies, you can confirm that the results are completely different even when bundling the same code.

Runtime Module System has the advantage of enabling dynamic imports and code splitting to load only necessary modules at runtime, and has good compatibility with legacy code because it's CommonJS-based. On the other hand, the ES6 module system can generate smaller bundles by removing unnecessary code at build time through static analysis.

Specific differences in output

Code structure:

  • Webpack: Maintains module-separated structure
  • Rollup: Merged into single flat code

Runtime overhead:

  • Webpack: Includes module system code (~2KB)
  • Rollup: Only pure code (~50 bytes) (advantage)

Tree shaking:

  • Webpack: unused function remains
  • Rollup: unused, multiply functions completely removed (advantage)

 

4.3. HMR Performance Differences

Hot Module Replacement (HMR) is a technology that replaces only changed modules without page refresh and reflects them immediately when source code changes are detected in development mode.

HMR is a technology that immediately reflects screen elements or components when developers change source code without state changes and without refresh. Rollup, which has faster development mode procedures, has the advantage of being faster than Webpack in terms of speed.

Of course, Webpack doesn't retranspile and rebundle all project files from scratch every time a specific file changes. If that were the case, HMR would take a very long time and create significant hardware load. Internally, it caches the JavaScript version of unchanged files and completed transpiling parts, retranspiling only changed parts and proceeding with bundling. Also, developers can optimize these caching settings according to their discretion. However, even the most well-optimized Webpack with caching can hardly be faster in HMR speed than Rollup, which has no absolute bundling process.

 

4.4. Build Time Differences

The results of cloning a JIRA Clone project from GitHub and bundling it with Webpack and Rollup respectively were as follows.

Measurement results:

  • Webpack: 2.3MB (Build time: 2.234 seconds)
  • Rollup: 1.6MB (Build time: 7.507 seconds)

Test project github link: Actual link will go here

Build speed was significantly faster with Webpack, but the final bundle size was smaller with Rollup.

While Vite and Rollup are generally known to have speed advantages, this test showed Webpack being more than 3 times faster. The main reasons for this result were legacy library dependencies (a project written in the past using many CommonJS-based libraries) and large-scale project optimization (Webpack's caching system working effectively in complex projects with many components). This indicates that it's difficult to assert that Vite or Rollup's build speed is always faster than CRA or Webpack, and build performance can vary depending on project scale and characteristics.

Looking at the final bundled file size, Rollup showed about 30% smaller results than Webpack, which can be seen as properly reflecting the characteristic differences between Runtime Module System and ES6 module system. Rollup's flat bundling and efficient tree shaking contributed to generating smaller bundles.

 

5. Webpack vs Rollup Final Results

Looking at the comparison above, you can see that in performance aspects like speed, file size, and build procedures, Webpack shows some weakness compared to Vite. This is somewhat natural since Vite was developed relatively recently compared to Webpack and incorporates more modern technologies.

Nevertheless, Webpack continues to be used in many projects as it has been consistently used for a long time. This long history means more than just the passage of time - it has significant meaning in that stability has been proven through numerous tests and extensibility is excellent with a vast plugin ecosystem. Also, the existence of diverse communities and contributors serves as a major advantage. Having stability, flexibility, and a community to discuss problems with during development are considered very important factors, so I think projects based on Webpack continue to be developed.

For this reason, especially in financial sectors or large corporations where stability is the top priority and complex requirements must be handled frequently, Webpack is still often preferred. On the other hand, in startups where deployments happen multiple times a day and fast development speed is important, or companies working on relatively simple structured projects that don't require complex build configurations, Vite is increasingly being used.

Of course, just because Webpack is more stable and has a larger community doesn't mean Vite is unstable or lacks community support. Each tool has its own advantages, and the important thing seems to be choosing the build tool that fits the nature and requirements of the current project.

 


6. Conclusion

In this article, we compared two representative transpilers and bundlers. Understanding internal operating methods helps not only user experience but also greatly improves developer experience. Knowing the characteristics of various tools enables better tool selection according to project situations.

Just because you're familiar with a currently used tool doesn't mean you don't need to know other tools at all. Having basic understanding of competing tools allows you to make better technical decisions. It's worthwhile enough in that it broadens the scope of technology choices.

I hope this article helped you feel more familiar with build tools. I hope it provided an opportunity to reflect on what roles the tools you usually use have played, and I hope it helps you decide what criteria to use when choosing tools for new projects in the future.

CNHur HyeonBin (Max)
Reponses  0