Code Splitting
Phân chia bundle JavaScript thành các phần nhỏ hơn, tải chỉ khi cần (ví dụ: dynamic import).
Code Splitting là gì?
Code Splitting là kỹ thuật chia nhỏ tập tin JavaScript (bundle) thành nhiều phần nhỏ hơn, sau đó tải từng phần chỉ khi cần — thay vì tải toàn bộ mã nguồn ngay lúc khởi động trang. Đây không phải là nén file hay tối ưu kích thước, mà là thay đổi cách tổ chức và phân phối mã để giảm khối lượng dữ liệu ban đầu.
Ví dụ: Khi người dùng truy cập trang chủ, ứng dụng chỉ tải mã xử lý giao diện chính; còn mã cho chức năng thanh toán hoặc quản lý tài khoản sẽ được tải riêng khi người dùng nhấn vào mục tương ứng — thường thông qua import() động (dynamic import).
Tại sao quan trọng trong SEO?
Google xếp hạng trang dựa một phần vào trải nghiệm người dùng trên thiết bị di động — đặc biệt là các chỉ số Core Web Vitals như Largest Contentful Paint (LCP), First Input Delay (FID) và Cumulative Layout Shift (CLS). Code Splitting giúp cải thiện trực tiếp:
- Giảm thời gian tải ban đầu: Bundle nhỏ hơn → ít bytes truyền qua mạng → LCP nhanh hơn;
- Giảm tắc nghẽn CPU: Ít mã JavaScript cần parse/compile ngay lập tức → FID thấp hơn;
- Tăng khả năng index: Trang tải nhanh hơn trên mạng chậm → bot Google dễ thu thập nội dung đầy đủ, nhất là với trang SPA (Single Page Application) có render phía client.
Theo báo cáo của HTTP Archive (2024), trang web có tổng kích thước JavaScript dưới 170 KB (trước nén) có xác suất đạt LCP < 2.5s cao gấp 3.2 lần so với trang có bundle > 500 KB — và Code Splitting là một trong ba phương pháp hiệu quả nhất để kiểm soát kích thước này (cùng lazy loading và tree shaking).
Cách hoạt động
Code Splitting dựa vào hai cơ chế chính:
- Phát hiện điểm chia: Nhà phát triển đánh dấu vị trí chia bằng cú pháp như
import('./module.js')hoặc cấu hình webpack/Rollup để tự động tách theo route hoặc vendor; - Tải động tại thời điểm chạy: Trình duyệt tạo yêu cầu
fetchriêng cho từng chunk khi gọi hàmimport(), trả về Promise, và thực thi module sau khi tải xong.
Chú ý: Các chunk được đặt tên, version hóa (thông qua content hash), và lưu cache độc lập — nên nếu chỉ sửa một module, chỉ chunk đó bị thay đổi, không ảnh hưởng đến cache của các chunk khác.
Hướng dẫn thực hiện
Dưới đây là các bước áp dụng Code Splitting phổ biến nhất, phù hợp với đa số dự án hiện đại:
- Xác định điểm chia hợp lý: Tập trung vào các phần không cần ngay lúc tải — ví dụ: component modal, form đăng ký, thư viện đồ họa nặng (Chart.js), hoặc toàn bộ route con (React Router, Vue Router);
- Sử dụng dynamic import: Thay
import Chart from 'chart.js'bằngconst { default: Chart } = await import('chart.js')trong hàm xử lý sự kiện hoặc lifecycle; - Cấu hình bundler:
- Webpack: bật
optimization.splitChunks(mặc định bật ở mode production); thêmchunks: 'all'vàcacheGroupsđể tách vendor; - Vite: hỗ trợ sẵn dynamic import + automatic code splitting — không cần cấu hình thêm;
- Rollup: dùng plugin
@rollup/plugin-dynamic-import-varsnếu cần import động theo biến.
- Webpack: bật
- Kiểm tra kết quả: Dùng tab Network trong DevTools → lọc
.js→ reload trang → kiểm tra số lượng và kích thước chunk; so sánh với bản chưa tách bằng công cụsource-map-explorerhoặcwebpack-bundle-analyzer.
Lỗi thường gặp
| Lỗi | Nguồn gốc | Cách khắc phục |
|---|---|---|
| Chunk bị tải trùng lặp | Nhiều nơi gọi import() cùng một module mà không cache |
Dùng hàm wrapper với Promise cache hoặc cấu hình splitChunks.cacheGroups để gộp chung |
| SSR thất bại với dynamic import | Server-side render không hỗ trợ import() (vì không có môi trường module động) |
Dùng React.lazy + Suspense (React) hoặc defineAsyncComponent (Vue); đảm bảo server trả về placeholder, client xử lý tải sau |
| Chunk không có hash → cache lỗi | Tên file chunk cố định (ví dụ: chunk.js) khiến CDN không nhận thay đổi |
Cấu hình bundler xuất tên dạng [name].[contenthash:8].js; kiểm tra output file trong dist/ |
Ví dụ thực tế
Một trang thương mại điện tử sử dụng React:
// Trước (tất cả tải ngay)
import ProductGallery from './components/ProductGallery';
import ReviewForm from './components/ReviewForm';
import PaymentGateway from './lib/PaymentGateway';
function ProductPage() {
return (
<div>
<ProductGallery />
<ReviewForm />
<PaymentGateway />
</div>
);
}
Sau khi áp dụng Code Splitting:
const ProductGallery = React.lazy(() => import('./components/ProductGallery'));
const ReviewForm = React.lazy(() => import('./components/ReviewForm'));
// PaymentGateway chỉ tải khi nhấn nút "Thanh toán"
const handleCheckout = async () => {
const { default: PaymentGateway } = await import('./lib/PaymentGateway');
new PaymentGateway().init();
};
Kết quả đo được trên Lighthouse (trên mạng 3G): LCP giảm từ 4.2s → 1.8s; kích thước JavaScript ban đầu giảm 68% (từ 842 KB → 269 KB).
Câu hỏi thường gặp
Code Splitting có làm chậm tính năng nào không?
Có thể làm chậm tính năng nếu tải chunk quá muộn — ví dụ: người dùng nhấn nút “Xem chi tiết” nhưng chunk chưa tải xong → xuất hiện độ trễ. Giải pháp: dùng prefetch (tải trước khi người dùng tương tác) hoặc preload (tải song song với render). Tuy nhiên, mức độ hiệu quả phụ thuộc vào hành vi người dùng thực tế — tùy trường hợp.
Có nên áp dụng Code Splitting cho mọi dự án?
Không bắt buộc. Dự án tĩnh (HTML/CSS thuần), trang landing đơn giản hoặc site có tổng JavaScript < 50 KB thường không cần. Code Splitting mang lại lợi ích rõ rệt nhất khi bundle lớn hơn 200 KB hoặc có nhiều tính năng không đồng thời sử dụng. Với ứng dụng doanh nghiệp hoặc SPA phức tạp — đây là thực hành bắt buộc.
Code Splitting khác gì với Lazy Loading?
Lazy Loading là khái niệm rộng hơn: bao gồm trì hoãn tải ảnh (loading="lazy"), iframe, font, và cả JavaScript. Code Splitting là một cơ chế kỹ thuật cụ thể để thực hiện Lazy Loading cho mã nguồn — tức là “lazy load JavaScript bằng cách chia nhỏ bundle”. Hai khái niệm thường đi kèm nhưng không đồng nghĩa.