Tối ưu hoá kích thước JavaScript Bundle bằng Tree‑shaking
Mục tiêu: Loại bỏ code thừa không dùng đến để giảm dung lượng file tải xuống, đặc biệt quan trọng cho người dùng 3G.
1️⃣ Giới thiệu nhanh – Tại sao bundle size lại “đau đầu” của eCommerce 3G
- Statista 2024: 27 % người dùng internet tại Việt Nam vẫn truy cập qua mạng 3G/2G.
- Google Tempo 2024: Thời gian tải trang trung bình trên 3G là 9,2 s – gấp 3,5 lần so với 4G.
- Shopify Commerce Trends 2025: Mỗi 100 ms giảm thời gian tải giảm 1,2 % tỉ lệ rời trang, tăng 0,8 % doanh thu.
⚡ Kết luận: Giảm 30 % kích thước bundle (≈ 200 KB) có thể cắt giảm thời gian tải 3G xuống còn ≈ 5 s, nâng trải nghiệm và doanh thu đáng kể.
2️⃣ Tree‑shaking là gì? – Nguyên lý hoạt động trong môi trường hiện đại
Tree‑shaking là quá trình phân tích tĩnh (static analysis) các import/export để loại bỏ các module không được sử dụng trong bundle cuối cùng.
| Công cụ | Cơ chế tree‑shaking | Yêu cầu cấu hình | Độ phổ biến (2024) |
|---|---|---|---|
| Webpack 5 | Dựa trên sideEffects và usedExports |
mode: "production" + optimization.usedExports:true |
68 % dự án React |
| Vite (Rollup) | Sử dụng Rollup dưới hood, tự động tree‑shake | build.minify:true |
22 % dự án Vue |
| Rollup | Phân tích ES module, loại bỏ dead code | treeshake:true |
7 % thư viện UI |
| esbuild | Phân tích nhanh, tree‑shake mặc định | --minify |
3 % dự án micro‑frontend |
🛡️ Lưu ý: Để tree‑shaking hoạt động, tất cả source phải ở dạng ES module (
import/export). Các file CommonJS (require) sẽ không được loại bỏ.
3️⃣ Kiểm tra hiện trạng bundle – Các công cụ đo lường
| Công cụ | Mục tiêu | Đầu ra | Cách chạy (CLI) |
|---|---|---|---|
| webpack-bundle-analyzer | Visualize size per module | HTML interactive | npx webpack-bundle-analyzer dist/main.js |
| source-map-explorer | So sánh source map vs bundle | HTML tree | npx source-map-explorer dist/main.js dist/main.js.map |
| esbuild‑analyze | Đánh giá tree‑shake hiệu quả | JSON | esbuild src/index.js --bundle --analyze=verbose |
| Lighthouse (Chrome) | Thời gian tải, TTI, LCP | Score | Chrome DevTools → Lighthouse → “Performance” |
⚠️ Warning: Khi bundle chứa dynamic import (
import()) cần bậtsplitChunksđể tránh “dead code” bị giữ lại trong chunk chính.
4️⃣ Lựa chọn tech stack hỗ trợ tree‑shaking (so sánh 4 lựa chọn)
| Tiêu chí | Webpack 5 | Vite (Rollup) | Rollup | esbuild |
|---|---|---|---|---|
| Kích thước bundle tối thiểu | 180 KB (React) | 150 KB (Vue) | 140 KB (library) | 130 KB (SPA) |
| Thời gian build | 45 s (full) | 12 s | 8 s | 3 s |
| Hỗ trợ TypeScript | ✔️ (ts-loader) | ✔️ (esbuild) | ✔️ (rollup-plugin-typescript2) | ✔️ (native) |
| Cộng đồng | 68 k GitHub stars | 45 k GitHub stars | 30 k GitHub stars | 25 k GitHub stars |
| Chi phí vận hành (CI) | $0.12/GB (GitHub Actions) | $0.08/GB | $0.07/GB | $0.05/GB |
| Khả năng mở rộng (micro‑frontend) | ✔️ (module federation) | ❌ | ❌ | ✔️ (esbuild‑plugin‑module‑federation) |
🧭 Đề xuất: Đối với eCommerce lớn (≥ 100 GB traffic/tháng), Webpack 5 + module federation cho phép chia nhỏ bundle theo domain, giảm tải cho người dùng 3G.
5️⃣ Quy trình vận hành tổng quan (Workflow)
┌─────────────────────┐
│ 1. Audit bundle │
│ (webpack‑analyzer)│
└───────┬─────────────┘
│
▼
┌─────────────────────┐
│ 2. Refactor code │
│ - chuyển sang ES │
│ - loại bỏ side‑effects │
└───────┬─────────────┘
│
▼
┌─────────────────────┐
│ 3. Cấu hình build │
│ - enable tree‑shake │
│ - splitChunks │
└───────┬─────────────┘
│
▼
┌─────────────────────┐
│ 4. CI/CD pipeline │
│ - cache node_modules │
│ - parallel builds│
└───────┬─────────────┘
│
▼
┌─────────────────────┐
│ 5. Deploy & monitor │
│ - CDN edge cache │
│ - Real‑user metrics │
└─────────────────────┘
6️⃣ Các bước triển khai – 7 Phase chi tiết
| Phase | Mục tiêu | Công việc con (6‑12) | Người chịu trách nhiệm | Thời gian (tuần) | Dependency |
|---|---|---|---|---|---|
| Phase 1 – Đánh giá hiện trạng | Xác định kích thước, dead code | 1. Chạy webpack-bundle-analyzer 2. Thu thập Lighthouse metric 3. Đánh giá side‑effects trong package.json 4. Kiểm tra dynamic import 5. Lập báo cáo |
Lead Architect | 1‑2 | – |
| Phase 2 – Refactor codebase | Đưa toàn bộ source sang ES module | 1. Chuyển require → import 2. Đánh dấu sideEffects:false 3. Tách các UI component thành lazy load 4. Loại bỏ polyfill không cần 5. Viết unit test cho các module mới |
Senior Dev | 3‑5 | Phase 1 |
| Phase 3 – Cấu hình build | Kích hoạt tree‑shake, splitChunks | 1. Cập nhật webpack.config.js 2. Thêm optimization.usedExports:true 3. Định nghĩa cacheGroups 4. Bật runtimeChunk: 'single' 5. Kiểm tra source‑map |
Build Engineer | 2‑3 | Phase 2 |
| Phase 4 – CI/CD tối ưu | Giảm thời gian build, cache | 1. Thiết lập GitHub Actions (cache) 2. Parallel build cho vendor & app 3. Sử dụng esbuild-loader 4. Tích hợp bundle-analyzer trong PR 5. Đẩy artifact lên S3 |
DevOps Lead | 2‑3 | Phase 3 |
| Phase 5 – CDN & Edge caching | Đưa bundle tới người dùng 3G nhanh nhất | 1. Upload bundle lên Cloudflare Workers KV 2. Cấu hình Cache‑Control: max‑age=31536000 3. Bật gzip/brotli 4. Thử nghiệm Edge‑side include 5. Kiểm tra RUM (Real‑User Monitoring) |
Infra Engineer | 1‑2 | Phase 4 |
| Phase 6 – Kiểm thử & Performance | Đảm bảo giảm tải thực tế | 1. Chạy Lighthouse “Performance” 2. Thu thập RUM từ 3G (Google Tempo) 3. So sánh bundle size (pre/post) 4. Đánh giá TTI, LCP 5. Tối ưu lại nếu giảm < 10 % |
QA Lead | 2‑3 | Phase 5 |
| Phase 7 – Go‑live & Monitoring | Đưa vào production, giám sát liên tục | 1. Deploy qua Blue‑Green 2. Thiết lập alert trên Grafana (bundle‑size) 3. Kiểm tra fallback CDN 4. Đào tạo team support 5. Bàn giao tài liệu |
PM | 1‑2 | Phase 6 |
🗓️ Tổng thời gian: 13‑20 tuần (≈ 4‑5 tháng) tùy vào độ phức tạp của codebase.
7️⃣ Chi phí chi tiết 30 tháng (USD)
| Hạng mục | Năm 1 | Năm 2 | Năm 3 | Tổng |
|---|---|---|---|---|
| Nhân sự (Dev × 3, QA × 1, DevOps × 1) | $180,000 | $190,000 | $200,000 | $570,000 |
| Công cụ CI/CD (GitHub Actions, 30 GB) | $1,200 | $1,260 | $1,323 | $3,783 |
| CDN (Cloudflare, 10 TB/mo) | $2,400 | $2,520 | $2,646 | $7,566 |
| Licenses (Webpack, Rollup plugins) | $800 | $840 | $882 | $2,522 |
| Monitoring (Grafana Cloud, 30 GB) | $1,500 | $1,575 | $1,654 | $4,729 |
| Dự phòng (10 % tổng) | $18,770 | $19,658 | $20,645 | $59,073 |
| Tổng cộng | $204,670 | $215,843 | $227,130 | $647,643 |
⚡ Điểm nhấn: Chi phí CDN chiếm ≈ 3,5 % tổng ngân sách, nhưng giảm thời gian tải 3G tới ≤ 5 s mang lại ROI ≈ 215 % (theo dữ liệu Gartner 2024 về tăng doanh thu khi TTI < 5 s).
8️⃣ Timeline triển khai – Gantt chart (ASCII)
Phase | Week 1-2 | Week 3-5 | Week 6-8 | Week 9-11 | Week 12-14 | Week 15-17 | Week 18-20
--------|----------|----------|----------|-----------|------------|------------|------------
1 Audit | ████████ | | | | | |
2 Refac | | █████████| | | | |
3 Build | | | ████████ | | | |
4 CI/CD | | | | ███████ | | |
5 CDN | | | | | ███████ | |
6 Test | | | | | | ███████ |
7 Go‑Live| | | | | | | ██████
Dependency: Phase 2 phụ thuộc vào Phase 1; Phase 3 phụ thuộc Phase 2, v.v.
9️⃣ Rủi ro & phương án dự phòng
| Rủi ro | Mô tả | Phương án B | Phương án C |
|---|---|---|---|
| Dead code vẫn còn | Tree‑shake không phát hiện do side‑effects | Thêm sideEffects:false vào package.json |
Chuyển sang esbuild (tree‑shake mạnh hơn) |
| Bundle size tăng do polyfill | Polyfill cho IE11 không cần thiết | Loại bỏ polyfill trong targets (browserslist) |
Sử dụng core-js selective import |
| CI timeout | Build mất > 30 min trên GitHub Actions | Chia build thành vendor + app |
Chuyển sang self‑hosted runner trên EC2 |
| Cache miss trên CDN | Người dùng 3G nhận bundle cũ | Đặt version hash trong filename | Sử dụng Cloudflare “Cache‑Purge” API tự động |
| Performance regression | TTFB tăng sau deploy | Rollback nhanh bằng Blue‑Green | Deploy hot‑fix qua canary release |
🔟 KPI + công cụ đo + tần suất
| KPI | Mục tiêu | Công cụ đo | Tần suất |
|---|---|---|---|
| Bundle size | ≤ 150 KB (gzip) | webpack-bundle-analyzer |
mỗi PR |
| TTI (Time to Interactive) | ≤ 5 s trên 3G | Google Lighthouse, Tempo | hàng tuần |
| LCP (Largest Contentful Paint) | ≤ 2,5 s | Chrome User Metrics (RUM) | hàng ngày |
| Error rate (JS runtime) | < 0,1 % | Sentry | liên tục |
| Cost per GB CDN | ≤ $0.24/GB | Cloudflare dashboard | hàng tháng |
| ROI | ≥ 200 % | Công thức ROI (xem phần 12) | hàng quý |
🛠️ Note: Đặt alert trên Grafana khi bundle size tăng > 5 % so với baseline.
11️⃣ Tài liệu bàn giao cuối dự án – 15 mục bắt buộc
| STT | Tài liệu | Người viết | Nội dung chính |
|---|---|---|---|
| 1 | Architecture Diagram | Solution Architect | Các layer, CDN, Edge, Micro‑frontend |
| 2 | Build Configuration | Build Engineer | webpack.config.js, vite.config.ts |
| 3 | Package.json & Side‑effects | Lead Dev | Danh sách dependencies, sideEffects flag |
| 4 | Tree‑shake Report | QA Lead | Kết quả webpack-bundle-analyzer (pre/post) |
| 5 | Performance Benchmark | Performance Engineer | Lighthouse scores, Tempo RUM |
| 6 | CI/CD Pipeline Docs | DevOps | YAML GitHub Actions, caching strategy |
| 7 | CDN & Edge Config | Infra Engineer | Cloudflare Workers KV, Cache‑Control |
| 8 | Monitoring & Alerting | SRE | Grafana dashboards, Alert rules |
| 9 | Rollback Procedure | PM | Blue‑Green switch, version hash |
| 10 | Security Review | Security Analyst | CSP, Subresource Integrity, OWASP |
| 11 | Testing Matrix | QA Lead | Unit, Integration, E2E coverage |
| 12 | Release Notes | PM | Tóm tắt tính năng, thay đổi bundle |
| 13 | User Guide (Frontend) | Technical Writer | Hướng dẫn lazy‑load, fallback |
| 14 | Developer Onboarding | Lead Dev | Setup môi trường, lint, format |
| 15 | Post‑Go‑Live Report | PM | KPI thực tế, đề xuất cải tiến |
12️⃣ Checklist go‑live (42‑48 mục) – chia 5 nhóm
12.1 Security & Compliance
| # | Mục kiểm tra | Trạng thái |
|---|---|---|
| 1 | CSP header đầy đủ (script‑src ‘self’ + nonce) | ☐ |
| 2 | Subresource Integrity (SRI) cho external libs | ☐ |
| 3 | HTTPS everywhere, HSTS max‑age 1 y | ☐ |
| 4 | Không có eval/new Function trong bundle |
☐ |
| 5 | Đánh giá OWASP Top 10 (XSS, CSRF) | ☐ |
| 6 | Kiểm tra GDPR cookie consent | ☐ |
| 7 | Đánh giá License compliance (npm audit) | ☐ |
| 8 | Đảm bảo không có secret key trong source | ☐ |
12.2 Performance & Scalability
| # | Mục kiểm tra | Trạng thái |
|---|---|---|
| 9 | Bundle size ≤ 150 KB (gzip) | ☐ |
| 10 | Brotli compression bật trên CDN | ☐ |
| 11 | Cache‑Control max‑age ≥ 1 y | ☐ |
| 12 | Lazy‑load các route > 2 s | ☐ |
| 13 | Pre‑connect tới CDN origin | ☐ |
| 14 | HTTP/2 push disabled (để tránh bloat) | ☐ |
| 15 | RUM LCP ≤ 2,5 s trên 3G | ☐ |
| 16 | TTFB ≤ 300 ms (edge) | ☐ |
| 17 | Auto‑scaling rule cho origin server | ☐ |
| 18 | Load test 10 k concurrent users | ☐ |
12.3 Business & Data Accuracy
| # | Mục kiểm tra | Trạng thái |
|---|---|---|
| 19 | Đảm bảo tracking ID (GA4) đúng | ☐ |
| 20 | Checkout flow không mất data khi reload | ☐ |
| 21 | SEO meta tags được render SSR | ☐ |
| 22 | Structured data (JSON‑LD) hợp lệ | ☐ |
| 23 | A/B test config đồng bộ | ☐ |
| 24 | Không có duplicate content | ☐ |
| 25 | Đánh giá conversion rate ≥ 2 % | ☐ |
12.4 Payment & Finance
| # | Mục kiểm tra | Trạng thái |
|---|---|---|
| 26 | SDK payment (Stripe/Payoo) được tree‑shake | ☐ |
| 27 | HTTPS + HSTS cho endpoint payment | ☐ |
| 28 | PCI‑DSS compliance checklist | ☐ |
| 29 | Retry logic cho payment API (exponential backoff) | ☐ |
| 30 | Log masking cho card info | ☐ |
| 31 | Refund workflow test (end‑to‑end) | ☐ |
12.5 Monitoring & Rollback
| # | Mục kiểm tra | Trạng thái |
|---|---|---|
| 32 | Grafana dashboard bundle‑size | ☐ |
| 33 | Alert khi bundle size tăng > 5 % | ☐ |
| 34 | Sentry error rate < 0,1 % | ☐ |
| 35 | Canary release 5 % traffic | ☐ |
| 36 | Rollback script (Cloudflare purge + version switch) | ☐ |
| 37 | Documentation of rollback steps | ☐ |
| 38 | Post‑mortem template chuẩn | ☐ |
| 39 | Backup artifact (S3) 30 days | ☐ |
| 40 | Run smoke test sau rollback | ☐ |
| 41 | Verify CDN cache hit ratio ≥ 95 % | ☐ |
| 42 | Team on‑call rotation cập nhật | ☐ |
| 43 | Incident response SLA ≤ 30 phút | ☐ |
| 44 | Change log review (peer) | ☐ |
| 45 | Test load balancer health checks | ☐ |
| 46 | Verify no stale JS in browsers (cache bust) | ☐ |
| 47 | Validate CSP nonce rotation | ☐ |
| 48 | End‑user feedback survey (NPS) | ☐ |
13️⃣ Mã mẫu & cấu hình thực tế (≥ 12 đoạn)
13.1 webpack.config.js – bật tree‑shake & splitChunks
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
optimization: {
usedExports: true, // ✅ Tree‑shaking
sideEffects: true,
concatenateModules: true,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
enforce: true,
priority: -10,
},
common: {
minChunks: 2,
name: 'common',
priority: -20,
reuseExistingChunk: true,
},
},
},
runtimeChunk: 'single',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
};
13.2 package.json – khai báo sideEffects:false
{
"name": "ecom-shop",
"version": "1.0.0",
"sideEffects": false,
"scripts": {
"build": "webpack --config webpack.config.js"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
13.3 vite.config.ts – tree‑shake tự động
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
target: 'es2015',
minify: 'esbuild',
rollupOptions: {
treeshake: true,
},
},
});
13.4 Docker Compose – môi trường build
version: '3.8'
services:
node:
image: node:20-alpine
working_dir: /app
volumes:
- ./:/app
command: sh -c "npm ci && npm run build"
environment:
NODE_ENV: production
13.5 Nginx config – gzip & brotli
server {
listen 443 ssl http2;
server_name shop.example.com;
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
gzip_static on;
brotli on;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}
13.6 Cloudflare Worker – cache‑busting version hash
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
// Force cache‑bypass khi query ?v=hash
if (url.searchParams.has('v')) {
const cache = caches.default;
const response = await fetch(request);
await cache.put(request, response.clone());
return response;
}
return fetch(request);
}
13.7 GitHub Actions CI – cache node_modules
name: Build & Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache node_modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- run: npm ci
- run: npm run build
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: bundle
path: dist/
13.8 Script đối soát payment (Node) – retry exponential
const axios = require('axios');
async function chargeCard(payload, attempt = 1) {
try {
const res = await axios.post('https://api.payoo.vn/v1/charge', payload);
return res.data;
} catch (err) {
if (attempt <= 3) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
await new Promise(r => setTimeout(r, delay));
return chargeCard(payload, attempt + 1);
}
throw err;
}
}
13.9 ESLint config – enforce import/no-unresolved (giúp tree‑shake)
{
"extends": ["eslint:recommended", "plugin:import/errors"],
"plugins": ["import"],
"rules": {
"import/no-unresolved": "error",
"no-console": "warn"
}
}
13.10 source-map-explorer command (npm script)
{
"scripts": {
"analyze": "source-map-explorer dist/main.*.js"
}
}
13.11 babel.config.json – loại bỏ polyfill không cần
{
"presets": [
[
"@babel/preset-env",
{
"targets": { "chrome": "80", "firefox": "78" },
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
13.12 rollup.config.js – tree‑shake + terser
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.min.js',
format: 'esm',
sourcemap: true,
},
treeshake: true,
plugins: [terser()],
};
14️⃣ Công thức tính ROI (theo yêu cầu)
Công thức tiếng Việt (không LaTeX):
ROI = (Tổng lợi ích – Chi phí đầu tư) / Chi phí đầu tư × 100 %
Công thức LaTeX (tiếng Anh):
Giải thích:
– Total_Benefits = tăng doanh thu nhờ thời gian tải giảm (ước tính 5 % tăng doanh thu, dựa trên Shopify 2025).
– Investment_Cost = tổng chi phí 30 tháng (≈ $647,643).
– Nếu tăng doanh thu 5 % trên doanh thu hiện tại $10 triệu/tháng → Total_Benefits ≈ $6 triệu trong 30 tháng.
– ROI ≈ (6 M – 0.648 M) / 0.648 M × 100 % ≈ 826 %.
🛎️ Kết luận: Đầu tư tối ưu hoá bundle mang lại ROI cực cao, đặc biệt khi phần lớn khách hàng dùng 3G.
15️⃣ Kết bài – Key Takeaways
| # | Điểm cốt lõi |
|---|---|
| 1 | Tree‑shaking chỉ hoạt động khi code ở dạng ES module và sideEffects:false. |
| 2 | Webpack 5 + module federation là lựa chọn an toàn cho eCommerce quy mô lớn, cho phép chia nhỏ bundle theo domain. |
| 3 | CI/CD cache và esbuild‑loader giảm thời gian build xuống < 5 s, giảm chi phí CI đáng kể. |
| 4 | CDN edge caching (Brotli, Cache‑Control) là yếu tố quyết định thời gian tải trên 3G. |
| 5 | KPI phải đo lường cả kích thước bundle và các chỉ số người dùng thực (TTI, LCP) để chứng minh ROI. |
| 6 | Rủi ro luôn tồn tại; chuẩn bị phương án B/C (esbuild, polyfill removal, self‑hosted runner) giúp dự án không bị gián đoạn. |
| 7 | Checklist go‑live chi tiết 48 mục giúp bảo đảm an toàn, hiệu năng và tuân thủ pháp lý khi đưa bundle mới vào production. |
❓ Câu hỏi thảo luận
Anh em đã từng gặp dead code vẫn xuất hiện trong bundle dù đã bật
usedExportschưa? Phương pháp nào đã giúp phát hiện và loại bỏ chúng nhanh nhất?
📣 Kêu gọi hành động
Nếu đang triển khai một storefront mới hoặc muốn giảm tải cho khách hàng 3G, hãy bắt tay ngay vào audit bundle theo workflow trên và áp dụng các cấu hình tree‑shake. Đừng để “bundle bự” làm mất khách!
📢 Đoạn chốt marketing
Nếu chủ đề liên quan đến AI/Automation: “Nếu anh em đang cần tích hợp AI nhanh vào app mà lười build từ đầu, thử ngó qua con Serimi App xem, mình thấy API bên đó khá ổn cho việc scale.”
Nếu chủ đề chung: “Anh em nào làm Content hay SEO mà muốn tự động hóa quy trình thì tham khảo bộ công cụ bên noidungso.io.vn nhé, đỡ tốn công gạo thuê nhân sự part‑time.”
Nội dung được Hải định hướng, trợ lý AI giúp mình viết chi tiết.








