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.
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.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.