Engineering
Build Tools
Vite, Webpack, esbuild, and modern bundling strategies
Build Tools
Build tools transform source code into optimized bundles for production. Modern tools focus on both developer experience and output quality.
Tool Comparison
| Tool | Dev Speed | Build Speed | Config | Ecosystem |
|---|---|---|---|---|
| Vite | Excellent | Fast | Simple | Growing |
| Webpack | Good | Medium | Complex | Mature |
| esbuild | N/A | Fastest | Minimal | Limited |
| Parcel | Good | Fast | Zero | Medium |
| Rollup | N/A | Fast | Medium | Mature |
Vite
Vite leverages native ES modules for instant dev server startup and HMR.
Basic Configuration
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
build: {
target: 'es2020',
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash-es', 'date-fns'],
},
},
},
},
css: {
modules: {
localsConvention: 'camelCase',
},
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`,
},
},
},
});Environment Variables
# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
# .env.development
VITE_API_URL=http://localhost:8080
# .env.production
VITE_API_URL=https://api.production.com// Usage in code
const apiUrl = import.meta.env.VITE_API_URL;
const isDev = import.meta.env.DEV;
const isProd = import.meta.env.PROD;Useful Plugins
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';
import compression from 'vite-plugin-compression';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
react(),
// Bundle analysis
visualizer({
filename: 'stats.html',
open: true,
}),
// Gzip compression
compression({
algorithm: 'gzip',
ext: '.gz',
}),
// PWA support
VitePWA({
registerType: 'autoUpdate',
manifest: {
name: 'My App',
short_name: 'App',
theme_color: '#ffffff',
},
}),
],
});Webpack
Webpack remains popular for complex applications requiring extensive customization.
Basic Configuration
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
mode: isDev ? 'development' : 'production',
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: isDev ? '[name].js' : '[name].[contenthash].js',
chunkFilename: isDev ? '[name].chunk.js' : '[name].[contenthash].chunk.js',
clean: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
],
},
{
test: /\.(png|jpg|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8kb
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
!isDev && new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
process.env.ANALYZE && new BundleAnalyzerPlugin(),
].filter(Boolean),
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
runtimeChunk: 'single',
},
devServer: {
port: 3000,
hot: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map',
};esbuild
esbuild is extremely fast, often used as a transpiler within other tools.
// build.js
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
minify: true,
sourcemap: true,
target: ['es2020'],
outfile: 'dist/bundle.js',
define: {
'process.env.NODE_ENV': '"production"',
},
loader: {
'.png': 'dataurl',
'.svg': 'text',
},
}).catch(() => process.exit(1));
// Watch mode
esbuild.context({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
}).then(ctx => ctx.watch());Code Splitting Strategies
Route-based Splitting
// React with React Router
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}Component-based Splitting
// Heavy component lazy loading
const HeavyChart = lazy(() => import('./components/HeavyChart'));
const RichTextEditor = lazy(() =>
import('./components/RichTextEditor').then(module => ({
default: module.RichTextEditor,
}))
);Manual Chunks
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('react')) return 'react-vendor';
if (id.includes('lodash')) return 'lodash-vendor';
return 'vendor';
}
},
},
},
},
});Asset Optimization
Image Optimization
// vite.config.ts
import viteImagemin from 'vite-plugin-imagemin';
export default defineConfig({
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 3 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.8, 0.9] },
svgo: {
plugins: [
{ name: 'removeViewBox', active: false },
],
},
}),
],
});Font Optimization
/* Preload critical fonts */
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter.woff2') format('woff2');
font-display: swap;
}<!-- In HTML head -->
<link rel="preload" href="/fonts/Inter.woff2" as="font" type="font/woff2" crossorigin>Best Practices
Build Optimization Tips
- Use native ES modules in development (Vite)
- Enable source maps only in development
- Configure proper caching with content hashes
- Split vendor code from application code
- Analyze bundle size regularly
- Use tree shaking to eliminate dead code
- Compress assets (gzip/brotli) in production