Buổi 15: Debug, Optimization & Best Practices
Loại buổi: Lý thuyết
Thời lượng: 120 phút
Dự án: To-Do App (đã refactor code từ buổi 14)
🎯 Mục tiêu học tập
Sau buổi học này, bạn sẽ có thể:
- ✅ Sử dụng Console để debug hiệu quả
- ✅ Sử dụng Chrome DevTools để debug
- ✅ Tối ưu hóa performance của ứng dụng
- ✅ Áp dụng best practices khi viết code
- ✅ Review code và phát hiện lỗi
- ✅ Sử dụng Git cơ bản để quản lý code
🧠 Nội dung chính
1. Debug với Console
1.1. Các phương thức console
// console.log() - In thông tin
console.log('Thông tin cơ bản');
console.log('Giá trị:', value);
console.log('Object:', { a: 1, b: 2 });
// console.warn() - Cảnh báo
console.warn('Đây là cảnh báo');
// console.error() - Lỗi
console.error('Đây là lỗi');
// console.table() - Hiển thị dạng bảng
let danhSach = [
{ id: 1, ten: 'CV 1', trangThai: 'chua lam' },
{ id: 2, ten: 'CV 2', trangThai: 'hoan thanh' }
];
console.table(danhSach);
// console.group() - Nhóm log
console.group('Thông tin sinh viên');
console.log('Họ tên: Nguyễn Văn A');
console.log('Tuổi: 20');
console.groupEnd();
// console.time() - Đo thời gian
console.time('thoiGianXuLy');
// Code cần đo
console.timeEnd('thoiGianXuLy');1.2. Debug với breakpoint
// Sử dụng debugger
function xuLyDuLieu(data) {
debugger; // Dừng tại đây khi mở DevTools
// Code xử lý
return data.map(item => item.value);
}1.3. Conditional logging
const DEBUG = true;
function log(message) {
if (DEBUG) {
console.log(message);
}
}
log('Thông tin debug');2. Chrome DevTools
2.1. Tab Console
- Xem log, error, warning
- Chạy JavaScript trực tiếp
- Kiểm tra giá trị biến
2.2. Tab Elements (Inspector)
- Xem và chỉnh sửa HTML/CSS
- Inspect element
- Xem computed styles
- Test responsive design
2.3. Tab Sources
- Xem source code
- Đặt breakpoint
- Step through code (F10, F11)
- Watch variables
- Call stack
Các phím tắt:
F8: ContinueF10: Step overF11: Step intoShift + F11: Step out
2.4. Tab Network
- Xem các request HTTP
- Kiểm tra response/request headers
- Xem timing
- Throttle network (test slow connection)
2.5. Tab Performance
- Record performance
- Phân tích thời gian load
- Tìm bottlenecks
2.6. Tab Application
- Xem LocalStorage, SessionStorage
- Xem Cookies
- Xem Cache
3. Tối ưu hóa Performance
3.1. Tránh re-render không cần thiết
// ❌ Xấu: Render lại toàn bộ danh sách
function themCongViec(ten) {
danhSachCongViec.push({ id: Date.now(), ten });
hienThiDanhSach(); // Render lại tất cả
}
// ✅ Tốt: Chỉ thêm phần tử mới
function themCongViec(ten) {
const congViec = { id: Date.now(), ten };
danhSachCongViec.push(congViec);
const li = taoElementCongViec(congViec);
danhSach.appendChild(li); // Chỉ thêm phần tử mới
}3.2. Debounce cho input
// Tránh gọi API quá nhiều khi user gõ
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const timKiem = debounce(function(query) {
console.log('Tìm kiếm:', query);
// Gọi API
}, 300);
// Sử dụng
input.addEventListener('input', (e) => {
timKiem(e.target.value);
});3.3. Lazy loading
// Chỉ load dữ liệu khi cần
async function taiDanhSach(page = 1) {
const response = await fetch(`/api/data?page=${page}`);
return await response.json();
}
// Load thêm khi scroll
window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
taiThemDuLieu();
}
});3.4. Cache dữ liệu
let cache = {};
async function layDuLieu(id) {
// Kiểm tra cache
if (cache[id]) {
return cache[id];
}
// Lấy từ API
const data = await fetch(`/api/data/${id}`).then(r => r.json());
cache[id] = data; // Lưu vào cache
return data;
}3.5. Minimize DOM operations
// ❌ Xấu: Nhiều lần truy cập DOM
function hienThiDanhSach() {
const ul = document.querySelector('#danh-sach');
ul.innerHTML = '';
danhSach.forEach(item => {
const li = document.createElement('li');
li.textContent = item.ten;
ul.appendChild(li); // Append nhiều lần
});
}
// ✅ Tốt: Dùng DocumentFragment
function hienThiDanhSach() {
const ul = document.querySelector('#danh-sach');
const fragment = document.createDocumentFragment();
danhSach.forEach(item => {
const li = document.createElement('li');
li.textContent = item.ten;
fragment.appendChild(li);
});
ul.innerHTML = '';
ul.appendChild(fragment); // Chỉ append 1 lần
}4. Best Practices
4.1. Error Handling
// ✅ Luôn xử lý lỗi
async function layDuLieu() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Lỗi:', error);
// Hiển thị thông báo cho user
hienThiThongBao('Không thể tải dữ liệu. Vui lòng thử lại.');
throw error; // Re-throw để caller xử lý
}
}4.2. Validation
// ✅ Validate input
function themCongViec(ten) {
if (!ten || typeof ten !== 'string') {
throw new Error('Tên công việc không hợp lệ');
}
if (ten.trim().length === 0) {
throw new Error('Tên công việc không được rỗng');
}
if (ten.length > 100) {
throw new Error('Tên công việc quá dài');
}
// Thêm công việc
}4.3. Code Comments
/**
* Thêm công việc mới vào danh sách
* @param {string} ten - Tên công việc
* @param {string} moTa - Mô tả công việc (optional)
* @returns {Object} Công việc đã được thêm
* @throws {Error} Nếu tên công việc không hợp lệ
*/
function themCongViec(ten, moTa = '') {
// Validate input
if (!ten || ten.trim().length === 0) {
throw new Error('Tên công việc không được rỗng');
}
// Tạo công việc mới
const congViec = {
id: Date.now(),
ten: ten.trim(),
moTa: moTa.trim(),
trangThai: 'chua lam',
ngayTao: new Date().toISOString()
};
// Thêm vào danh sách
danhSachCongViec.push(congViec);
return congViec;
}4.4. Avoid Magic Numbers
// ❌ Xấu
if (tuoi >= 18) {
// ...
}
// ✅ Tốt
const TUOI_TRUONG_THANH = 18;
if (tuoi >= TUOI_TRUONG_THANH) {
// ...
}4.5. Early Returns
// ❌ Xấu
function xuLyDuLieu(data) {
if (data) {
if (data.length > 0) {
// Xử lý
}
}
}
// ✅ Tốt
function xuLyDuLieu(data) {
if (!data || data.length === 0) {
return;
}
// Xử lý
}5. Code Review Checklist
Functionality:
- [ ] Code hoạt động đúng như mong đợi?
- [ ] Xử lý edge cases?
- [ ] Xử lý lỗi đầy đủ?
Code Quality:
- [ ] Code dễ đọc, dễ hiểu?
- [ ] Tên biến/hàm rõ ràng?
- [ ] Không có code lặp lại?
- [ ] Functions nhỏ, làm 1 việc?
Performance:
- [ ] Không có re-render không cần thiết?
- [ ] Không có memory leaks?
- [ ] Tối ưu DOM operations?
Best Practices:
- [ ] Sử dụng const/let thay vì var?
- [ ] Arrow functions khi phù hợp?
- [ ] Async/await thay vì Promise chains?
- [ ] Destructuring khi có thể?
6. Git cơ bản
6.1. Khởi tạo repository
git init6.2. Các lệnh cơ bản
# Xem trạng thái
git status
# Thêm file vào staging
git add .
# Commit
git commit -m "feat: thêm chức năng thêm công việc"
# Xem lịch sử
git log
# Tạo branch
git branch feature/the-cong-viec
# Chuyển branch
git checkout feature/the-cong-viec
# Merge branch
git merge feature/the-cong-viec6.3. Commit message
Conventional Commits:
feat: thêm tính năng mới
fix: sửa lỗi
refactor: refactor code
docs: cập nhật tài liệu
style: format code
test: thêm test
chore: công việc bảo trìVí dụ:
feat(todo): thêm chức năng tìm kiếm công việc
- Thêm input tìm kiếm
- Filter danh sách theo từ khóa
- Highlight kết quả tìm kiếm
Closes #123💻 Ví dụ minh họa
Ví dụ 1: Debug với console
function xuLyDanhSach(danhSach) {
console.group('Xử lý danh sách');
console.log('Số lượng:', danhSach.length);
console.table(danhSach);
console.time('thoiGianXuLy');
const ketQua = danhSach.map(item => {
console.log('Xử lý item:', item);
return item.value * 2;
});
console.timeEnd('thoiGianXuLy');
console.groupEnd();
return ketQua;
}Ví dụ 2: Performance optimization
// Tối ưu render danh sách lớn
function hienThiDanhSach(danhSach) {
const container = document.querySelector('#danh-sach');
const fragment = document.createDocumentFragment();
// Virtual scrolling cho danh sách lớn
const start = 0;
const end = Math.min(50, danhSach.length); // Chỉ render 50 items đầu
for (let i = start; i < end; i++) {
const li = taoElementCongViec(danhSach[i]);
fragment.appendChild(li);
}
container.innerHTML = '';
container.appendChild(fragment);
}🧪 Quiz cuối buổi (7 câu)
Câu 1: debugger làm gì?
A. Dừng code tại đó khi DevTools mở
B. In ra console
C. Xóa code
D. Không làm gì
Đáp án: A
Câu 2: Debounce dùng để làm gì?
A. Tăng tốc độ
B. Giảm số lần gọi hàm
C. Xóa code
D. Debug
Đáp án: B
Câu 3: DocumentFragment dùng để?
A. Cache dữ liệu
B. Tối ưu DOM operations
C. Lưu trữ
D. Debug
Đáp án: B
Câu 4: Early return giúp?
A. Code chạy nhanh hơn
B. Code dễ đọc hơn
C. Tiết kiệm bộ nhớ
D. Tất cả
Đáp án: B (chủ yếu là dễ đọc)
Câu 5: Commit message nào đúng?
A. fixed bug
B. fix: sửa lỗi thêm công việc
C. update
D. change
Đáp án: B
Câu 6: console.table() dùng để?
A. In object dạng bảng
B. Tạo table HTML
C. Lưu dữ liệu
D. Không có
Đáp án: A
Câu 7: Best practice nào đúng?
A. Dùng var thay vì let/const
B. Functions làm nhiều việc
C. Early returns
D. Magic numbers
Đáp án: C
📝 Bài tập về nhà (chuẩn bị cho buổi 16)
- Review code To-Do App và fix các vấn đề
- Tối ưu performance (debounce, DocumentFragment...)
- Thêm error handling đầy đủ
- Thêm comments cho các hàm phức tạp
- Commit code lên Git với commit message chuẩn
- Chuẩn bị demo và thuyết trình
🔗 Tài liệu tham khảo
Chúc bạn học tập tốt! 🚀