Tối ưu JavaScript
Minify JavaScript
Vite tự động minify JS khi build (dùng esbuild/Terser):
bash
vite build # JS tự động minified + mangledĐể kiểm soát hơn:
js
// vite.config.ts
export default defineConfig({
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // xóa console.log trong production
drop_debugger: true,
},
},
},
})Non-blocking JavaScript
html
<!-- Xấu — block HTML parsing -->
<script src="/app.js"></script>
<!-- defer — tải song song, chạy sau khi HTML parse xong, giữ thứ tự -->
<script src="/app.js" defer></script>
<!-- async — tải song song, chạy ngay khi tải xong (không đảm bảo thứ tự) -->
<script src="/analytics.js" async></script>
<!-- Module script — mặc định defer -->
<script type="module" src="/app.js"></script>Khi nào dùng gì:
defer— script phụ thuộc DOM hoặc cần thứ tựasync— script độc lập (analytics, ads, chat widget)- Đặt
<script>cuối<body>nếu không dùng defer/async
Tránh nhiều inline script
html
<!-- Xấu — nhiều inline script rải rác trong HTML -->
<script>var x = 1</script>
<div>...</div>
<script>var y = 2</script>
<div>...</div>
<script>console.log(x + y)</script>
<!-- Tốt — gộp vào 1 file external -->
<script src="/app.js" defer></script>Vấn đề với nhiều inline script:
- Không cache được
- Parser phải dừng lại mỗi lần gặp
<script> - Khó maintain
Kiểm tra vấn đề hiệu năng trong JS
Long Tasks
Task chạy > 50ms trên main thread sẽ block UI — user thấy trang bị lag/freeze.
js
// Tách task nặng thành chunks nhỏ
async function processLargeArray(items) {
const CHUNK_SIZE = 100
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
const chunk = items.slice(i, i + CHUNK_SIZE)
processChunk(chunk)
// Nhường main thread sau mỗi chunk
await new Promise(resolve => setTimeout(resolve, 0))
}
}Web Workers
Chuyển tác vụ nặng sang worker thread — không block UI:
js
// worker.js
self.onmessage = ({ data }) => {
const result = heavyComputation(data)
self.postMessage(result)
}
// main.js
const worker = new Worker('/worker.js')
worker.postMessage(largeData)
worker.onmessage = ({ data }) => console.log('Result:', data)requestIdleCallback
Chạy task khi browser rảnh:
js
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
tasks.shift()() // chạy 1 task
}
})Giữ dependencies cập nhật
Dependencies cũ có thể chứa bug hiệu năng đã được fix ở phiên bản mới:
bash
# Kiểm tra dependencies lỗi thời
npm outdated
# Update
npm update
# Xem security vulnerabilities
npm auditKiểm tra
DevTools > Performance tab > Record > xem Long Tasks (màu đỏ trên timeline). Mục tiêu: không có task nào > 50ms.