Câu Chuyện Về Dòng Code "Nguy Hiểm" Mà 90% React Developer Đang Mắc Phải

Câu Chuyện Về Dòng Code "Nguy Hiểm" Mà 90% React Developer Đang Mắc Phải

Khi một dòng code có thể Phá Hủy trải nghiệm người dùng?

Hãy tưởng tượng anh em đang xây dựng một trang landing page tuyệt đẹp. Mọi thứ hoàn hảo - animation mượt mà, typography sắc nét, và một thư viện ảnh background ấn tượng từ CMS. Anh em deploy lên production, tự hào khoe với sếp.

Rồi một ngày, khách hàng phàn nàn: "Trang web load chậm kinh khủng!" 🤡

Tôi biết thể nào anh em cũng sẽ check network tab, nhưng sẽ không thấy vấn đề gì. Bundle size vẫn ổn. Vậy vấn đề nằm ở đâu?

Câu trả lời có thể khiến anh em bất ngờ: chính là trong những dòng code tưởng chừng "vô hại" mà anh em viết mỗi ngày.

Code "Vô Hại" Nhưng Cực Kỳ Nguy Hiểm 😳

Hãy xem đoạn code này - có thể anh em đã viết nó hàng trăm lần:

Trông quen thuộc phải không? Đơn giản, sạch sẽ, dễ hiểu. Nhưng đây chính là "quả bom hẹn giờ" đang đếm ngược trong ứng dụng mà anh em đang phát triển.

Anh em đã phát hiện ra vấn đề chưa? 🤔🤔🤔

Nếu chưa, đừng lo. Thì chúng ta cần phải đi sâu vào "bộ não" của trình duyệt để hiểu tại sao đoạn code này lại nguy hiểm đến vậy.

💡
Plot twist: 70% performance issues KHÔNG phải do JavaScript mà do... images! 🤯

🛣 Hành Trình Của Browser: Từ HTML Đến Pixels

Khi Browser Nhận Được HTML:

Mỗi khi browser nhận một file HTML, nó bắt đầu một quá trình như sau:

  1. Parser HTML khởi động - Như một người đọc sách, browser đọc từ trên xuống dưới, xây dựng cây DOM từng bước một.
  2. Khi Gặp thẻ <script>? - Parser dừng lại ngay lập tức! Nó phải chờ script và tải và thực thi xong mới tiếp tục. Đây là lý do chúng ta dùng deferasync (nhưng đó là câu chuyện khác).
  3. Gặp thẻ <link> cho CSS? - Thì điều gì sẽ xảy ra?

CSS Parser - Người Hùng Thầm Lặng:

Khi gặp stylesheet, browser không dừng parse HTML. Thay vào đó, nó khởi động một "nhân vật" mới: CSS Parser.

CSS Parser có nhiệm vụ quan trọng:

  • Xây dựng cây CSSOM (CSS Object Model)
  • Cho phép JavaScript tương tác với styles
  • Hoàn thành Critical Rendering Path - quá trình quan trọng để biến code thành pixels trên màn hình

Nhưng CSS Parser có một điểm yếu chết người...

Khi CSS Parser gặp external link (như url() trong CSS), nó phải:

  • Nó KHÔNG dừng lại để download ngay!
  • Thay vào đó, nó note lại "cần download image này"
  • CSSOM tree vẫn được xây bình thường
  • Images chỉ được tải khi browser cần render element đó

Nhưng đây mới là vấn đề thực sự...

Tại Sao Code Của Bạn Lại Nguy Hiểm? ☠️

Quay lại đoạn code ban đầu:

Khi bạn map qua 10 images và set chúng làm background, điều gì xảy ra?

Browser thực sự làm gì:

  1. Parse HTML/CSS → Xây cây DOM/CSSOM bình thường
  2. Khi cần render các elements → Phát hiện cần 10 images
  3. Gửi 10 HTTP requests CÙNG LÚC (song song)

Hậu quả là:

  1. HTTP Connection Limits - Nếu với HTTP/1.1, Browser giới hạn 6-8 kết nối mỗi domain. 10 images = bottleneck ngay! (Lưu ý: HTTP/2 với multiplexing giảm thiểu vấn đề này nhưng lại gặp vấn đề bandwidth - xem điểm 4)
  2. Không có Lazy Loading - Tất cả images tải ngay lập tức, kể cả những cái ngoài viewport. User chưa scroll tới đã phải tải về! 🤦
  3. Inline Styles = Khó Optimize và Reuse - Inline styles khó maintain, không tận dụng được các kỹ thuật tối ưu CSS optimization. Images vẫn được cache dựa trên URL nhưng việc quản lý styles trở nên phức tạp hơn.
  4. Bandwidth Competition - Dù download song song, nhưng với nhiều ảnh lớn, 10 ảnh lớn cùng download sẽ "chia nhau" bandwidth, làm TẤT CẢ đều chậm.

Kết quả: First Contentful Paint bị delay, Largest Contentful Paint tăng vọt, và user experience "tê liệt" - đặc biệt trên mobile/3G.

Đó là lý do tại sao khách hàng phàn nàn về tốc độ load. Network tab không show vấn đề vì requests vẫn parallel, nhưng tổng thời gian chờ mới là thủ phạm!

Bài Học Rút Ra: Performance Không Chỉ Là Bundle Size

Nhiều developer tập trung vào:

  • Code splitting
  • Tree shaking
  • Minification
  • ......

Nhưng lại quên mất rằng cách trình duyệt hoạt động mới là yếu tố then chốt.

Một đoạn code tưởng chừng vô hại có thể:

  • Tạo ra hàng chục yêu cầu HTTP không cần thiết
  • Tải các tài nguyên khi người dùng chưa cần
  • Làm tắc nghẽn network bandwidth
  • Phá hủy Core Web Vitals (LCP, FCP, CLS)

Vậy giải pháp là gì?

Đây là một câu hỏi dành cho anh em nhé 😉

Vậy trong dự án hiện tại, anh em có bao nhiêu chỗ đang dùng backgroundImage với external URLs? Có thể đó chính là "thủ phạm" làm chậm sản phẩm của anh em đó, sửa ngay đi nha 😙😙😙

Còn rất nhiều thứ hay ho mà chúng mình muốn chia sẻ thêm, nếu bạn là một React Developer đang cần học thêm để lên Senior thì hãy tham khảo khóa học cùng giảng viên từng làm Senior tại Shopee Singapore để học hỏi những kinh nghiệm thực tế từ big tech nha:

React Nâng Cao - Chuyên Sâu với Tối Ưu Hiệu Năng
Khóa học React từ cơ bản đến nâng cao với chuyên gia. Trở thành Senior React Developer với Sydexa.

Học cùng người giỏi nhất sẽ giúp bạn đi nhanh nhất!