Performance
Code Splitting
Load code on demand to improve performance
Intermediate
performance bundling webpack lazy-loading
Definition
Code splitting is the practice of splitting your JavaScript bundle into smaller chunks that can be loaded on demand. This reduces the initial bundle size, improving Time to Interactive (TTI) and overall performance.
Dynamic Imports
Basic Syntax
// Instead of static import
import { add } from './math';
console.log(add(2, 3));
// Use dynamic import
import('./math').then(math => {
console.log(math.add(2, 3));
});
// With async/await
async function loadMath() {
const math = await import('./math');
return math.add(2, 3);
}
Conditional Loading
async function loadFeature(featureName) {
if (featureName === 'chart') {
const { Chart } = await import('./Chart');
return Chart;
}
if (featureName === 'map') {
const { Map } = await import('./Map');
return Map;
}
}
React.lazy and Suspense
Route-Based Splitting
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Lazy load route components
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const Dashboard = lazy(() => import('./routes/Dashboard'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Component-Level Splitting
import { lazy, Suspense, useState } from 'react';
// Lazy load heavy component
const HeavyChart = lazy(() => import('./HeavyChart'));
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>
Show Chart
</button>
{showChart && (
<Suspense fallback={<div>Loading chart...</div>}>
<HeavyChart />
</Suspense>
)}
</div>
);
}
Named Exports
Wrapping Named Exports
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// Component wrapper for default export
export const MathOperations = {
add,
subtract
};
// Lazy import with named exports
const LazyMath = lazy(() => import('./math').then(module => ({
default: module.MathOperations
})));
Preloading Strategies
Prefetch on Hover
function LinkWithPrefetch({ to, children }) {
const handleMouseEnter = () => {
// Prefetch route on hover
const About = import('./routes/About');
};
return (
<a
href={to}
onMouseEnter={handleMouseEnter}
>
{children}
</a>
);
}
Webpack Magic Comments
// Prefetch chunk
const Admin = lazy(() => import(
/* webpackChunkName: "admin" */
/* webpackPrefetch: true */
'./Admin'
));
// Preload chunk (higher priority)
const Dashboard = lazy(() => import(
/* webpackChunkName: "dashboard" */
/* webpackPreload: true */
'./Dashboard'
));
Bundle Analysis
// webpack-bundle-analyzer
// Install: npm install --save-dev webpack-bundle-analyzer
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
Best Practices
Split by Route
// Each major route gets its own chunk
const routes = [
{
path: '/',
component: lazy(() => import('./Home'))
},
{
path: '/products',
component: lazy(() => import('./Products'))
},
{
path: '/cart',
component: lazy(() => import('./Cart'))
}
];
Split Large Libraries
// Load heavy libraries only when needed
async function generatePDF() {
const { jsPDF } = await import('jspdf');
const doc = new jsPDF();
doc.text('Hello World', 10, 10);
doc.save('document.pdf');
}
Key Takeaway
Code splitting improves performance by loading JavaScript on demand. Use dynamic imports for route-based and component-level splitting, wrap lazy components with Suspense, and preload strategically. Analyze your bundle regularly to identify splitting opportunities.