Hiểu hơn về phân trang - pagination
Phân trang - một thành phần không thể thiếu trong các ứng dụng có lượng dữ liệu lớn. Tuy nhiên, bạn hiểu được bao nhiêu về nó?
Phân trang là gì?
Đơn giản nó là cách chúng ta chunk dữ liệu thành từng page nhỏ trong trường hợp lượng dữ liệu lớn và hầu như không thể hiển thị hết trên một trang.
Về phía backend, khi có quá nhiều mục cần tải sẽ làm quá tải hệ thống, gây ảnh hưởng tới kết nối của những người dùng khác. Đây là một vấn đề lớn về hiệu suất.
Các loại phân trang thường gặp
Theo kinh nghiệm sử dụng của mình thì mình chia pagination thành 3 loại.
1. Numbered pages
Hay còn gọi là phân trang có đánh số. Google Search đang sử dụng cách phân trang này.
Phân trang đánh số này thường được sử dụng trong trường hợp các dữ liệu thường không có sự thay đổi quá thường xuyên.
Khi bạn nói với ai đó "Ồ, tôi đang mở trang thứ 3" thì bạn đang mong muốn dữ liệu ở trang thứ 3 này nhất quán ở cả phía bạn và người khác.
Hình thức phân trang này cũng có cái lợi là người dùng sẽ biết được trang cuối cùng là bao nhiêu (mặc dù mình nghĩ chẳng bao giờ họ navigate tới tận trang cuối đó).
2. Sequential pages
Hay còn gọi là phân trang tuần tự.
Thường sử dụng cho các trang web hay ứng dụng trên điện thoại do không gian nhỏ, hoặc khi mà dữ liệu người dùng thay đổi nhanh đến mức việc đánh số trang chẳng có ý nghĩa gì nữa.
3. Infinite scroll
Cuộn, cuộn, cuộn và cuộn.
Hình thức này kết hợp với Loading Skeleton tạo ảo giác trang của bạn rất dài, và nội dung được hiển thị liên tục.
Lúc này, người dùng không còn quan tâm tới việc đang ở trang nào, họ chỉ cần cuộn để có thêm nội dung.
Ví dụ đơn giản nhất là các trang Twitter, Facebook, Reddit, Tiktok...
Có vẻ như hầu hết các ứng dụng hiện đại ngày nay đều sử dụng cách 2 hoặc 3 (đôi khi kết hợp chúng với nhau) khi mà có càng nhiều thuật toán render dữ liệu người dùng thay đổi liên tục, điều này khiến cho việc đánh số trang càng ngày càng mất ý nghĩa.
Mặc dù cách phân trang đánh số thứ tự thường không hữu dụng mấy cho các ứng dụng hiện đại so với các mô hình phân trang còn lại, tuy nhiên nó lại là cách dễ triển khai nhất trong các hệ thống cơ sở dữ liệu :D
Numbered pages
Ai đã dùng SQL trước đây đều biết cách implement nó một cách đơn giản như sau:
// Chúng ta muốn load nội dung cho page thứ 3, giới hạn mỗi page 100 phần tử
// ==> Chúng ta cần load 100 phần tử, bắt đầu từ sau phần tử thứ 200
SELECT * FROM users ORDER BY id DESC LIMIT 100 OFFSET 200;
Khi bạn muốn biết tổng số phần tử của các trang, bạn có thể thực hiện thêm một câu truy vấn khác:
SELECT COUNT(*) AS total_items FROM users;
Khá là đơn giản đúng không :D
Nhiều ứng dụng vẫn sử dụng cách phân trang này cho dù nó không có bao nhiêu ý nghĩa. Thử nghĩ mà xem, bạn chỉ cần cung cấp 2 tham số skip
và limit
là xong chuyện.
Để triển khai với REST, bạn chỉ cần thêm 1 tham số page vào query params là xong (mặc định Laravel dùng cách này).
GET http://localhost:3008/users?page=2
Để triển khai với GraphQL cũng khá đơn giản:
{
users(page: 2) {
id
name
roles {
id
name
}
}
}
Hạn chế của việc đánh số trang
Đối với các nội dung tĩnh hoặc ít khi thay đổi, đánh số trang là phương pháp tuyệt vời. Nhưng đối với các ứng dụng hiện đại ngày nay, việc đánh số trang đã không còn phù hợp. Các items đôi khi được thêm vào hoặc dịch chuyển đi mất trong khi người dùng đang nhảy tới các trang khác nhau.
- Bỏ qua một vài items quan trọng: đôi khi người dùng sẽ bỏ qua mất một vài mục quan trọng khi mà chúng được thêm vào hoặc chuyển đi trong khi người dùng đang nhảy qua các trang.
- Hiển thị một mục 2 lần: tương tự, do nội dung giữa các trang có thể bị thay đổi khi chúng ta thêm vào hoặc xóa đi một vài items, người dùng có thể thường xuyên gặp tình trang nhìn thấy một số items nhiều lần.
Có một giải pháp nào tốt nhất không?
Thực ra, không có cách nào là tốt nhất cả. Cái bạn cần quan tâm là đối tượng sử dụng ứng dụng của bạn là ai, và lúc này bạn sẽ lựa chọn hướng xử lý tốt nhất về UX cho các đối tượng đó.
Như blog này của mình, dữ liệu đâu được bao nhiêu, vậy nên mình vẫn sử dụng cách đơn giản nhất là phân trang có đánh số :D.
Trên tất cả, cần có một sự cân bằng giữa việc đơn giản hoá quá trình thực hiện - cải thiện hiệu suất - cũng như UX đẹp mắt, dễ sử dụng.