Một vài “mẹo nhỏ” tối ưu React mà nhiều dev hay bỏ qua
kỹ thuật như DNS prefetch, preconnect, prefetch.
Một vài “mẹo nhỏ” tối ưu React mà nhiều dev hay bỏ qua
Khi làm dự án React, đa số chúng ta tập trung vào code component, state, hooks, mà ít để ý những tối ưu network ở tầng HTML/HTTP có thể giúp app mượt hơn hẳn. https://www.youtube.com/shorts/vihz1MAzx1c một số kỹ thuật như xóa DNS cache, dùng DNS prefetch, preconnect, prefetch và refresh lại app React để cảm nhận khác biệt.
Trong bài này, mình tóm lại và triển khai sâu hơn một vài khái niệm hay ho xuất hiện quanh video đó, dưới góc nhìn một lập trình viên front-end.
DNS prefetch là gì và nên dùng khi nào?
Khi trình duyệt cần tải resource từ một domain mới, nó phải làm DNS lookup, tốn thêm thời gian cho request đầu tiên. Thuộc tính dns-prefetch cho phép trình duyệt “đi trước một bước”, resolve sẵn DNS cho một domain mà bạn biết chắc sẽ dùng, ví dụ:
xml<link rel="dns-prefetch" href="//example-cdn.com" />
Khi user thực sự cần tải ảnh, font, hay script từ example-cdn.com, bước DNS lookup gần như đã xong, giảm được latency cho request đầu. Tuy nhiên, nếu bạn spam dns-prefetch cho quá nhiều domain, trình duyệt sẽ phải resolve DNS cho hàng loạt host mà có khi user không bao giờ dùng đến, gây lãng phí tài nguyên.
Preconnect: mạnh hơn dns-prefetch nhưng không phải lúc nào cũng tốt
preconnect đi xa hơn dns-prefetch: nó không chỉ DNS lookup mà còn thiết lập TCP handshake, thậm chí TLS handshake, tới server từ sớm. Ví dụ:
xml<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
Với những resource quan trọng, như font, CDN ảnh chính, API critical, preconnect giúp giảm đáng kể thời gian tới byte đầu tiên (TTFB) cho request thực tế. Nhưng nếu bạn dùng preconnect tràn lan cho nhiều domain, trình duyệt phải mở nhiều kết nối TCP/TLS cùng lúc, dễ lãng phí socket, CPU và memory, nhất là trên mobile.
Một điểm thú vị được giải thích trong phần comment video:
Nếu bạn “spam”
preconnectlên nhiều thẻ<img>nhưng tất cả đều dùng chung một domain, trình duyệt vẫn chỉ mở một kết nối cho domain đó.Vấn đề chỉ thực sự tệ nếu bạn
preconnecttới quá nhiều domain khác nhau; khi đó hiệu quả giảm, và chi phí tăng.
Lời khuyên hợp lý: chỉ dùng preconnect cho vài resource quan trọng nhất (CDN chính, font, API critical), còn với nhiều domain phụ thì chỉ cần dns-prefetch là đủ.
Prefetch: chuẩn bị sẵn tài nguyên “có khả năng sẽ cần”
prefetch được dùng khi bạn dự đoán user có thể sẽ cần một resource nào đó trong tương lai gần, ví dụ script cho một route khác, hay ảnh của step tiếp theo. Ví dụ:
xml<link rel="prefetch" href="/static/next-page-chunk.js" as="script" />
Browser sẽ tải resource này thấp ưu tiên, không block rendering hiện tại, nhưng khi user điều hướng tới trang đó, file đã sẵn trong cache nên tải gần như tức thì. Tuy vậy, nếu bạn “prefetch mọi thứ”, bạn đang chiếm băng thông cho những thứ user có thể không bao giờ dùng đến, đặc biệt nguy hiểm trên mạng chậm.
Trong ngữ cảnh React, prefetch thường kết hợp với code-splitting và lazy loading route để tối ưu UX: user chạm tới đâu, gần như luôn thấy cảm giác “instant”.
Áp dụng vào dự án React thực tế
Gợi ý một số điểm bạn có thể check ngay trong dự án:
Kiểm tra domain tài nguyên chính
Font, CDN ảnh, API chính… có thể thêmdns-prefetchhoặcpreconnectchọn lọc trongindex.html(create-react-app) hoặc_document.tsx(Next.js).Phân loại “critical” và “non-critical”
Resource ảnh hero, font chính, JS bundle chính: cân nhắcpreload/preconnect.
Resource cho bước tiếp theo, route tiếp theo: dùngprefetchhoặc cơ chế prefetch của router (ví dụ Next.jsnext/linkprefetch).Tránh spam trên quá nhiều domain
Nếu ảnh từ cùng một CDN, không cần gánpreconnectlên từng<img>. Chỉ cần một lần ở<head>là đủ.Đo lường trước và sau
Dùng Lighthouse, WebPageTest, devtools để đo metrics như TTFB, First Contentful Paint, Largest Contentful Paint trước và sau khi thêm các hint này, để xem chúng có thực sự giúp app React nhanh hơn không.
Góc Q&A: Có nên “spam” preconnect/prefetch cho mọi ảnh?
Trong phần bình luận, có bạn hỏi: “Preconnect và prefetch có khuyết điểm gì không? Nếu mình spam 2 cái đấy cho mọi img thì có sao không anh?” Đây là câu hỏi rất thực tế, vì nhiều người sau khi biết tới resource hint thường có xu hướng áp dụng quá tay.
1. Spam trên cùng một domain thì sao?
Nếu bạn gắn preconnect hoặc dns-prefetch cho rất nhiều thẻ <img>, nhưng tất cả resource đều đến từ cùng một domain (ví dụ https://cdn.example.com), trình duyệt thực tế chỉ cần thiết lập kết nối cho domain đó một lần. Nghĩa là, bạn không “nhân” chi phí kết nối lên theo số lượng ảnh, mà chủ yếu đang tạo thêm HTML noise, khó maintain hơn.
Tuy vậy, về mặt code clean, tốt hơn là khai báo preconnect/dns-prefetch một lần ở <head> thay vì rải rác trên từng thẻ <img>, vừa rõ ràng, vừa dễ kiểm soát.
2. Khuyết điểm lớn nhất: nhiều domain khác nhau
Vấn đề bắt đầu rõ rệt khi bạn “spam” preconnect trên nhiều domain khác nhau. Mỗi preconnect là một TCP (và có thể TLS) handshake riêng tới từng server, tiêu tốn:
Socket/connection trên client
CPU để handshake
Memory để giữ connection mở trong một khoảng thời gian
Nếu bạn chỉ truy cập rất ít trong số các domain đó, phần lớn kết nối sớm trở thành lãng phí resource. Vì vậy, lời khuyên hợp lý là: chỉ dùng preconnect cho vài resource quan trọng nhất như CDN chính, font, hoặc API critical mà bạn biết chắc sẽ được gọi sớm.
3. Khi nào nên ưu tiên dns-prefetch?
Trong trường hợp bạn vẫn phải làm việc với nhiều domain (ví dụ: nhiều host ảnh khác nhau, third-party), dns-prefetch là lựa chọn nhẹ nhàng và an toàn hơn. Nó chỉ resolve DNS (rẻ hơn nhiều so với thiết lập full TCP/TLS connection), giúp rút bớt latency cho request đầu, nhưng không “ôm” quá nhiều kết nối mở như preconnect.
Một cách thực dụng:
Ít domain, rất quan trọng →
preconnectNhiều domain, mức độ ưu tiên không cao →
dns-prefetch