Why Bundle?
Browsers can load ES modules directly, but bundlers optimize your code for production: combine files, minify code, tree-shake unused code, transpile modern JS for older browsers, and handle non-JS assets like CSS and images.
Vite — The Modern Choice
// npm create vite@latest my-app -- --template vanilla
// npm install
// npm run dev → dev server with HMR
// npm run build → production build
// vite.config.js:
import { defineConfig } from "vite";
export default defineConfig({
build: {
outDir: "dist",
minify: true,
rollupOptions: {
input: "index.html"
}
},
server: {
port: 5173,
proxy: {
"/api": "http://localhost:3000" // dev proxy
}
}
});
// Vite features:
// - Dev server with instant HMR (no rebuild)
// - ESM-first
// - TypeScript out of the box
// - CSS Modules, PostCSS
// - Dynamic imports + code splitting
// - Fast builds via Rollup
Webpack — The Veteran
// npm install -D webpack webpack-cli
// webpack.config.js:
const path = require("path");
module.exports = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.[contenthash].js" // cache busting
},
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader", // transpile modern JS
exclude: /node_modules/
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
optimization: {
splitChunks: { chunks: "all" } // code splitting
}
};
// Tree shaking — automatically removes unused code:
// If you import { add } from "./math.js" and never use subtract,
// webpack/rollup removes subtract from the bundle entirely
Code Splitting
// Dynamic imports create separate chunks:
async function loadAnalytics() {
const { default: Analytics } = await import("./analytics.js");
Analytics.init();
}
// Route-based splitting (common in SPAs):
const HomePage = () => import("./pages/Home.js");
const AboutPage = () => import("./pages/About.js");
// Only loads code when needed — faster initial load!
// Preload critical chunks:
const link = document.createElement("link");
link.rel = "modulepreload";
link.href = "/assets/critical-chunk.js";
document.head.appendChild(link);
⚡ Key Takeaways
- Bundlers combine, minify, and optimize code for production
- Vite: modern, fast dev server with instant HMR — great for new projects
- Webpack: mature, highly configurable — common in larger enterprises
- Tree shaking removes unused code — keep exports specific
- Code splitting via dynamic import() reduces initial bundle size
- Always analyze bundle with tools like bundlesize or source-map-explorer
🎯 Practice Exercises
EXERCISE 1
Set up a Vite project with TypeScript. Add a route-based code splitting: lazily load a "Dashboard" module only when the user navigates to /dashboard.