Frontend Development 24 min read

Understanding Vite: What It Is, How It Works, and Its Differences from Webpack

This article explains what Vite is, distinguishes it from traditional scaffolding tools, details its underlying build‑tool principles, compares Vite with Webpack, and walks through its core architecture, plugin system, hot‑module replacement, and why it offers fast, on‑demand development experiences.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Understanding Vite: What It Is, How It Works, and Its Differences from Webpack

What is Vite

Vite is often mistakenly called just a Vue scaffolding tool, but it can scaffold Vue, React, and Svelte projects alike. It belongs to the category of build tools that transform source files into browser‑compatible modules.

Scaffolding vs Build Tools

Scaffolding (or project generators) are command‑line utilities that quickly create a project’s boilerplate code, provide best‑practice templates, and allow custom templates. Build tools, on the other hand, automate the conversion of modern code (e.g., ES2024 syntax) into code that browsers can understand, handling bundling, compilation, and hot updates.

Differences

Scaffolding creates a project structure; build tools package the project. In practice a scaffolding tool often includes a build tool, and traditional build tools can also generate scaffolding.

Other Build Tools

Before Vite, the ecosystem included:

Browserify, Grunt, Gulp (early tools)

Webpack, Rollup, Parcel (traditional bundlers)

Esbuild, Vite (modern, fast tools)

Among them, Webpack remains the most widely used.

Differences Between Vite and Webpack

Lightning‑fast startup

Hot Module Replacement (HMR)

On‑demand compilation

Vite focuses on improving the development experience, while Webpack has a richer plugin ecosystem but a slower startup. Vite’s ecosystem is newer, relying on Rollup plugins, whereas Webpack’s plugin ecosystem is extensive.

Vite Principles

In development Vite serves source files using native <script type="module"> support in modern browsers, dynamically loading modules on demand. However, Vite still needs to transform source files (e.g., Vue SFCs) into ESM‑compatible JavaScript before the browser can execute them.

Example of a simple HTML entry:

<div id="app"></div>
<script type="module">
  import { createApp } from 'vue'
  import Main from './Main.vue'
  createApp(Main).mount('#app')
</script>

When the browser requests this file, Vite resolves the vue import, compiles the .vue component to JavaScript, and serves the transformed module.

How Vite Handles CSS

Vite converts CSS files into JavaScript modules that inject styles into the DOM at runtime:

import "/Main.vue?type=style&index=0&scoped=...&lang.css";
import { updateStyle as __vite__updateStyle, removeStyle as __vite__removeStyle } from "/@vite/client";
const __vite__id = "/path/to/Main.vue?type=style&...";
const __vite__css = "\n.comments[data-v-...]{\n  color: red;\n}\n";
__vite__updateStyle(__vite__id, __vite__css);
import.meta.hot.accept();
import.meta.hot.prune(() => __vite__removeStyle(__vite__id));

Thus Vite’s core principle is to turn every asset into an ESM module that the browser can import.

Source Code Walkthrough

The following sections outline the simplified implementation of a minimal Vite‑like server (named mini‑vite ), focusing on server creation, compilation, HMR, and performance.

1. How to Build a Web Server

Vite can use any lightweight HTTP framework such as koa , express , or connect . The example uses connect :

const app = connect();
const root = process.cwd();
const startTime = Date.now();
const plugins = resolvePlugins();
const pluginContainer = createPluginContainer(plugins);
const moduleGraph = new ModuleGraph(url => pluginContainer.resolveId(url));
const watcher = chokidar.watch(root, { ignored: ['**/node_modules/**', '**/.git/**'], ignoreInitial: true });
const ws = createWebSocketServer(app);
const serverContext = { root, app, pluginContainer, plugins, moduleGraph, ws, type: type == 'react' ? 'react' : 'vue', watcher };
bindingHMREvents(serverContext);
app.use(transformMiddleware(serverContext));
app.use(indexHtmlMiddware(serverContext));
app.use(staticMiddleware(serverContext.root));
app.listen(3000, async () => {
  await optimize(root, type == 'react' ? 'src/main.tsx' : 'src/main.ts');
  console.log('🚀 No‑Bundle service started!', `Time: ${Date.now() - startTime}ms`);
  console.log('> Local: http://localhost:3000');
});

2. How to Handle Compilation

Vite’s plugin system mirrors Rollup’s. A minimal JSON plugin example:

export function jsonPlugin(options = {}, isBuild) {
  return {
    name: 'vite:json',
    transform(json, id) {
      if (!jsonExtRE.test(id) || SPECIAL_QUERY_RE.test(id)) return null;
      json = stripBomTag(json);
      try {
        if (options.stringify) {
          if (isBuild) {
            return { code: `export default JSON.parse(${JSON.stringify(JSON.stringify(JSON.parse(json)))})`, map: { mappings: '' } };
          } else {
            return `export default JSON.parse(${JSON.stringify(json)})`;
          }
        }
        const parsed = JSON.parse(json);
        return { code: dataToEsm(parsed, { preferConst: true, namedExports: options.namedExports }), map: { mappings: '' } };
      } catch (e) {
        const position = extractJsonErrorPosition(e.message, json.length);
        this.error(`Failed to parse JSON file${position ? `, invalid JSON syntax at position ${position}` : '.'}`, position);
      }
    }
  };
}

The plugin’s transform method receives source code and returns transformed code (or an object with code and map ).

3. How HMR Works

Vite uses chokidar to watch file changes and a WebSocket server ( ws ) to push updates to the client.

const watcher = chokidar.watch(root, { ignored: ['**/node_modules/**', '**/.git/**'], ignoreInitial: true });
const wss = new WebSocketServer({ port: HMR_PORT });
watcher.on('change', async file => {
  console.log(`✨ [hmr] ${file} changed`);
  await serverContext.moduleGraph.invalidateModule(file);
  ws.send({ type: 'update', updates: [{ type: 'js-update', timestamp: Date.now(), path: '/' + getShortName(file, root), acceptedPath: '/' + getShortName(file, root) }] });
});

On the client side, the WebSocket receives the update message and re‑imports the changed module with a cache‑busting query:

async function fetchUpdate({ path, timestamp }) {
  const mod = hotModulesMap.get(path);
  if (!mod) return;
  try {
    const newMod = await import(`${path}?t=${timestamp}`);
    // apply HMR logic here
  } catch (e) {}
}

4. Why Vite Is Fast and On‑Demand

Traditional bundlers like Webpack pre‑bundle the entire application before serving, causing long startup times. Vite serves source files directly and compiles them on demand using native ESM support, eliminating the upfront bundling cost and providing instant feedback during development.

Conclusion

The article demonstrates Vite’s architecture, its plugin system, HMR implementation, and why it delivers a faster, on‑demand development experience compared to classic bundlers.

frontend developmentbuild-toolviteHot Module ReplacementWebpack Comparison
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.