Hệ thống chậm ư? Áp dụng ngay những kỹ thuật này (Phần 1)
Dạo bài
Một hệ thống lớn luôn phải đối mặt với sự gia tăng không ngừng của lượng truy cập từ người dùng, đồng thời phải kiểm soát được lượng dữ liệu ngày càng phình ra. Bạn bắt đầu nhận thấy những dấu hiệu rõ ràng của sự trì trệ, hệ thống chạy chậm và gây khó khăn trong trải nghiệm của người dùng
Trong bài viết này, mình sẽ giới thiệu một số kỹ thuật hữu ích để tối ưu hóa và cải thiện hiệu suất, giúp hệ thống của bạn hoạt động một cách hiệu quả hơn. Let's go!
💭 Đây không phải là một bài viết hướng dẫn triển khai chi tiết từng kỹ thuật
Client
1. Cache
Là một trong những kỹ thuật ưu tiên và quan trọng mà bạn nên áp dụng ngay và liền cho hệ thống của mình
Sử dụng bộ nhớ cache để lưu trữ dữ liệu tạm thời và giảm số lần gửi yêu cầu đến máy chủ. Trình duyệt có 3 cách chính để lưu trữ dữ liệu tạm thời, bao gồm:
- Local Storage: Dữ liệu được lưu trữ trên trình duyệt không có thời gian hết hạn và vẫn giữ lại ngay cả khi bạn tắt tab hoặc tắt trình duyệt
- Session Storage: Dữ liệu được lưu trữ trên trình duyệt, tuy nhiên khi bạn tắt tab hoặc tắt trình duyệt, dữ liệu sẽ bị xóa
- Cookies: Dữ liệu được lưu trữ trên trình duyệt ngay cả khi bạn tắt tab hoặc tắt trình duyệt. Với Cookies, dữ liệu lưu trữ bị hạn chế dung lượng và bạn có thể cài đặt thời hạn lưu trữ trong bao lâu
2. Choose Data structure & Algorithm
Đây được xem là một trong những yếu tố quan trọng khi xây dựng hệ thống, nhằm nâng cao hiệu suất, tăng tốc độ chạy và tối ưu hiệu quả ứng dụng của bạn
Việc chọn sai cấu trúc dữ liệu và thuật toán có thể khiến hệ thống chậm đi, hiệu suất giảm
Đặt vấn đề: Cho một mảng gồm các số nguyên và một target, tìm vị trí của các số trong mảng thỏa mãn điều kiện 2 số đó tổng bằng target
Cách giải đơn giản là tạo 2 vòng for và loop qua từng phần
const twoSum = function (nums = [2, 7, 11, 15], target = 9) {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) return [i, j];
}
}
};
Kết quả thực thi là 188 ms và 41.7 MB
Cách 2 là cách giải dựa trên cấu trúc dữ liệu hash table và chỉ loop một vòng for
const twoSum = function (nums = [2, 7, 11, 15], target = 9) {
const hashTable = {};
for (let i = 0; i < nums.length; i++) {
if (hashTable[target - nums[i]] !== undefined) {
return [hashTable[target - nums[i]], i];
}
hashTable[nums[i]] = i;
}
};
Kết quả thực thi là 55 ms và 45.7 MB
So sánh 2 kết quả ở trên thì cách thứ 2 tối ưu hiệu suất hơn cách 1 rất nhiều. Vì vậy, xem xét và áp dụng cấu trúc dữ liệu và thuật toán phù hợp sẽ giúp cải thiện hiệu quả hệ thống của bạn
3. Tree shaking & Bundle code
Sử dụng các trình biên dịch như Vite, Webpack,... sẽ giúp bạn loại bỏ những đoạn code không dùng, giảm kích thước file và đồng thời đóng gói các file của bạn để triển khai ứng dụng
4. Code splitting
Khác với bundle code (đóng gói lại mã nguồn để giảm số lần request), thì với code splitting, mã nguồn của bạn sẽ được phân tách và chia nhỏ, chỉ tải những tệp cần thiết khi cần thiết, giảm thời gian tải và thực thi mã nguồn. Các trình biên dịch như Vite, Webpack,... đều hỗ trợ kỹ thuật này
5. Async/defer script
Sử dụng thuộc tính async hoặc defer khi import các tệp script vào HTML để tăng tốc độ tải trang
Có thể bạn đã biết hoặc chưa, khi trình duyệt phân tích tới thẻ script, nó sẽ block lại quá trình render HTML để tải và thực thi Javascript. Điều này sẽ gây khó chịu cho user của bạn. Bởi vì vậy, các thẻ Javascript cần đặt ở cuối thẻ body hoặc nếu đặt ở trong header thì cần dùng tới thuộc tính async hoặc defer
<script async src="script1.js">
<script defer src="script2.js">
6. Optimize image
Trang web với nhiều hình ảnh có thể khiến cho trang của bạn chậm đi và tăng thời gian tải trang. Để giải quyết vấn đề này, bạn có thể áp dụng kỹ thuật lazy loading image để chỉ tải các hình ảnh khi cần thiết, tức là khi người dùng cuộn trang đến gần vị trí của hình ảnh thì lúc này hình ảnh mới được tải
<img src="image.png" loading="lazy" alt="Image" width="200" height="200">
Đảm bảo rằng kích thước hình ảnh được thiết kế sao cho phù hợp với kích thước hiển thị trên trang web
Sử dụng các công cụ nén hình ảnh để giảm kích thước tệp mà vẫn giữ được chất lượng hình ảnh
Chọn đúng định dạng hình ảnh cho từng trường hợp. Ví dụ, sử dụng định dạng JPEG cho hình ảnh có màu sắc phức tạp và PNG cho hình ảnh có độ trong suốt hoặc biểu đồ
7. Pagination
Khi user truy cập, sử dụng phân trang để hiển thị một phần nội dung trong trang sẽ giúp tăng tốc độ tải trang và giảm tải cho server
Một vài chiến lược trong kỹ thuật phân trang như:
- Basic pagination (Phân thành từng trang)
- Infinite scrolling (Cuộn vô hạn)
- Load more (Tải thêm)
8. Debounce & Throttle
Debounce và throttle là hai kỹ thuật được sử dụng để kiểm soát tần suất thực thi của các hàm trong quá trình xử lý sự kiện trong lập trình
Debounce là một hàm được gọi liên tục và sau đó được dừng gọi sau bao nhiêu ms thì hàm đó mới thật sự được thực thi
Ví dụ: Khi người dùng gõ vào ô tìm kiếm trên trang web, hàm xử lý tìm kiếm có thể được gọi liên tục mỗi khi người dùng gõ một ký tự. Tuy nhiên, để tránh việc gọi liên tục, ta có thể sử dụng debounce để chờ một khoảng thời gian sau khi người dùng dừng gõ ký tự, thì hàm sẽ chính thức thực thi và xử lý tìm kiếm từ khóa
Throttle giới hạn số lần gọi hàm trong bao nhiêu ms (bao nhiêu là do mình định nghĩa)
Ví dụ: Khi người dùng cuộn trang web, sự kiện scroll sẽ gọi và thực thi hàm liên tục. Tuy nhiên, để tránh tình huống đó, ta có thể sử dụng throttle để giới hạn hàm chỉ được thực thi sau mỗi 200ms chẳng hạn
9. Using CDN
Để cải thiện tốc độ tải trang và trải nghiệm người dùng, sử dụng mạng phân phối nội dung (CDN) là một lựa chọn thông minh
CDN là một hệ thống máy chủ phân tán đặt ở nhiều vị trí địa lý khác nhau trên thế giới. Khi người dùng truy cập trang web của bạn, CDN sẽ tải các tài nguyên (như hình ảnh, tệp CSS và Javascript) từ máy chủ gần vị trí của họ nhất
Đây là các nhà cung cấp dịch vụ CDN mà bạn có thể lựa chọn:
- Amazon CloudFront
- Google Cloud CDN
- Microsoft Azure CDN
- Cloudflare
- jsDelivr (Dành cho Javascript)
- UNPKG (Dành cho Javascript)
- cdnjs.com (Dành cho Javascript)
10. Using Web Workers
Web Workers là một công nghệ trong Web API của trình duyệt, cho phép bạn thực hiện các tác vụ chạy ngầm trên trang web. Nó hoạt động như một loại worker độc lập, không ảnh hưởng đến luồng chính của trang web
11. Using Tech & Library
Bằng cách lựa chọn công nghệ và thư viện phù hợp, bạn có thể tiết kiệm thời gian và công sức trong quá trình phát triển, đồng thời đảm bảo hiệu suất tốt nhất cho ứng dụng của mình
Một số library/framework dành cho frontend phổ biến như:
- ReactJS
- NextJS
- VueJS
- Svelte
12. Performance Testing & Evaluation
Thực hiện thử nghiệm và đánh giá hiệu suất của trang web để xác định các vấn đề và những chỗ bị thắt cổ chai trong quá trình tải trang. Sử dụng các công cụ như Google Lighthouse, WebPageTest,... để đo và phân tích hiệu suất của trang web, từ đó tìm hiểu và cải thiện các vấn đề hiệu suất cụ thể
Sử dụng Google Lighthouse phân tích Youtube:
- Mở https://youtube.com
Inspect
trình duyệt, và chuyển quaLighthouse
tab, chọnAnalyze page load
- Kết quả:
- Có thể thấy Youtube tập trung khá nhiều vào tối ưu Accessibility và SEO
Kết bài
Các kỹ thuật trên sẽ giúp bạn tối ưu hóa hệ thống và cải thiện hiệu suất, mang lại trải nghiệm tốt hơn cho người dùng. Tuy nhiên, cần lưu ý rằng mỗi hệ thống có yêu cầu và đặc điểm riêng, vì vậy hãy xem xét và áp dụng những kỹ thuật phù hợp với hệ thống của bạn. Trong phần tiếp theo Phần 2, mình sẽ chia sẻ những kỹ thuật tối ưu liên quan đến phía server. Cảm ơn các bạn đã đọc
All rights reserved