7 điểm bởi GN⁺ 2025-05-15 | 1 bình luận | Chia sẻ qua WhatsApp
  • Critical CSS là phần CSS tối thiểu cần thiết để render "vùng hiển thị đầu tiên (above the fold)" của trang, và khi được inline vào HTML thì có thể cải thiện Core Web Vitals như FCP (First Contentful Paint)
  • Được inline vào <head> của HTML để trình duyệt có thể render nội dung nhanh mà không cần chờ toàn bộ stylesheet tải xong
  • Có các lợi ích như cải thiện tốc độ tải được cảm nhận, tăng điểm Lighthouse, cải thiện SEO và UX
  • CSS không thiết yếu có thể được tải bằng <link> ở cuối <body>, hoặc lazy load bằng JavaScript để tối ưu hiệu năng hơn nữa
  • Cần lưu ý rằng người dùng phải tự điều chỉnh đường dẫn liên kết CSS và các tham chiếu tài sản

Critical CSS Generator

  • Critical CSS Generator là công cụ trích xuất chỉ phần mã CSS tối thiểu thực sự cần thiết từ một trang web, cho phép lấy CSS được tối ưu theo đúng mục đích sử dụng
  • Critical CSS là tập quy tắc CSS tối thiểu cần để style vùng hiển thị đầu tiên của trang
  • Cách làm này giúp trình duyệt hiển thị ngay nội dung chính mà không phải chờ tải xong mọi stylesheet, từ đó cải thiện hiệu năng và Core Web Vitals (như FCP)

Vì sao nên dùng?

  • Tốc độ tải ban đầu được cảm nhận nhanh hơn
  • Cải thiện điểm Lighthouse
  • Cải thiện SEO và trải nghiệm người dùng

🔧 Cách áp dụng

Step 1: Inline Critical CSS

  • Chèn Critical CSS vào trong thẻ <style> và đặt ở trên cùng trong phần <head> của HTML
  • Phải đặt trước các stylesheet hoặc script khác
  • Đường dẫn asset nội bộ cần được chỉnh sửa tùy theo nhu cầu
    <style>  
      /* Critical CSS for your page */  
      /* ... CSS content ... */  
    </style>  
    

Step 2: Tải trễ CSS không thiết yếu (cách cơ bản)

  • Xóa thẻ <link> gốc khỏi <head>đặt ngay trước </body>
  • Làm như vậy thì quá trình render ban đầu sẽ chỉ dùng Critical CSS, còn CSS không thiết yếu sẽ được tải sau
    <html>  
      ...  
      <body>  
        ...  
        <link rel="stylesheet" href="/css/vendors.min.css">  
        <link rel="stylesheet" href="/css/style.min.css">  
      </body>  
    </html>  
    

Step 3 (tùy chọn): Tải stylesheet bất đồng bộ bằng JavaScript

  • Sau khi trang tải xong, dùng JavaScript để tải động CSS không thiết yếu
  • Có thể cải thiện hiệu năng khi tốc độ mạng chậm
  • Cần xóa tất cả thẻ <link> CSS không thiết yếu hiện có khỏi <head>
    window.addEventListener("DOMContentLoaded", function () {  
      console.log("✅ Page loaded, now loading non-critical stylesheets...");  
      let stylesheets = [  
        // "/css/vendors.min.css",  
        // "/css/style.min.css",  
      ];  
      let loadedCount = 0;  
      function checkAllStylesLoaded() {  
        loadedCount++;  
        if (loadedCount === stylesheets.length) {  
          console.log("✅ All non-critical stylesheets loaded...");  
        }  
      }  
      stylesheets.forEach(function (href) {  
        var link = document.createElement("link");  
        link.rel = "stylesheet";  
        link.href = href;  
        link.onload = checkAllStylesLoaded;  
        link.onerror = () => console.warn(`Failed to load stylesheet: ${href}`);  
        document.head.appendChild(link);  
      });  
    });  
    

1 bình luận

 
GN⁺ 2025-05-15
Ý kiến Hacker News
  • Sẽ rất tuyệt nếu còn hỗ trợ cả responsive; vì rất khó dedupe critical style cho responsive nên cuối cùng tôi vẫn luôn phải tự sửa stylesheet bằng tay. Vì kích thước của critical CSS rất quan trọng, sẽ hay hơn nếu có cả tùy chọn down-compile những thứ như biến CSS. Ngoài ra, tôi không khuyến nghị lời khuyên đặt thẻ <link> của CSS không quan trọng ngay trước </body>, vì CSS cần được tải về càng nhanh càng tốt; cách này làm chậm việc phát hiện CSS nên việc tải xuống cũng bị muộn theo. Thay vào đó tôi khuyên dùng cách kết hợp preload và noscript: <link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="styles.css"></noscript>

    • Tôi có thắc mắc là cách dùng preload này kèm mã JS có bị chặn nếu CSP không cho phép unsafe-inline hay không.

    • Tôi không muốn dùng kiểu tải CSS bằng mẹo JS; khi áp dụng stylesheet, toàn bộ trang có thể phải tính toán lại layout/style. Trình duyệt vẫn tải stylesheet rất nhanh dù nó nằm ở cuối trang.

    • Chỉ với thuộc tính prefetch, các hint ở HTTP header và kết hợp CDN cũng có thể đạt hiệu quả tương tự. Không nhất thiết phải liên tục rebuild critical CSS. Nếu dùng CDN như CF cho đúng cách thì tốc độ cực nhanh.

    • Nghe có lý đấy. Tôi cũng dự định thêm tùy chọn như vậy. Thay vì before body tôi đã thử tùy chọn DOMCONTENTLOADED, và ngay cả trên điện thoại cũ hay mạng chậm thì UX và Lighthouse vẫn hoạt động đủ tốt.

    • Hoàn toàn đồng ý với ý kiến cần hỗ trợ responsive.

  • Theo tôi thì đây có vẻ là tối ưu hóa quá sớm. Nó chỉ đáng giá khi CSS cực kỳ phức tạp hoặc có rất nhiều tài nguyên phải tải, còn trong đa số trường hợp thì viết CSS, HTML, JS cho gọn gàng sẽ hiệu quả hơn, và cách này có thể là không cần thiết hoặc thậm chí phản tác dụng.

    • Cực kỳ hữu ích. Tôi làm freelancer và thường nhận các website WordPress; nhiều trường hợp sau khi qua tay nhiều dev/agency thì CSS/JS trở thành một mớ hỗn độn. Tôi thật sự muốn thử công cụ kiểu này.

    • CSS càng phức tạp hay tài nguyên càng nhiều thì hiệu quả ròng của kiểu tối ưu này lại càng nhỏ. Mục tiêu ở đây là tối ưu RTT latency, nhưng critical CSS càng lớn thì chi phí tối ưu càng tăng nên lợi ích ròng càng giảm.

    • Nếu là 12 năm trước thì tôi đã bỏ tiền cho công cụ như thế này. Khi đó có một lượng CSS khổng lồ tích lũy qua nhiều năm và rất khó biết quy tắc nào là critical.

    • Với nhiều website thì đúng là tối ưu hóa sớm, nhưng với những site nhạy cảm với lượt nhấp như tin tức/truyền thông thì tải trang “ngay lập tức” là cực kỳ quan trọng. Chỉ cần quá 1 giây là tỷ lệ thoát và doanh thu quảng cáo đã giảm. HuffPost cũng từng thử kiểu tối ưu này từ 10 năm trước.

    • Nhìn vào tình hình phát triển frontend hiện nay thì tôi thấy thật sự quá đà. Những công cụ như Lighthouse khiến người ta ám ảnh với việc tối ưu cho các con số vô nghĩa, kết quả là chỉ làm tăng độ phức tạp của build trong khi người dùng thực tế cũng chẳng cảm nhận được, còn việc phát triển thì khó chịu hơn. Trong khi đó lại thường xuyên thấy những website đầy lỗi UI/quản lý state rất cơ bản. Thật bức bối.

    • CSS, HTML, JS gọn gàng đúng là đáp án, nhưng đôi khi bạn phải tiếp quản một dự án lộn xộn hoặc dùng template, và ngay cả khi tự phát triển thì thiết kế cũng có thể rối lên.

    • Với tôi, cách tải CSS là hạng mục bắt buộc phải cân nhắc ngay từ giai đoạn đầu phát triển. Web của chúng tôi từng có điểm page speed test thấp khiến khách hàng rời bỏ rất nhiều. Hiệu năng ảnh hưởng đến SEO nên cực kỳ quan trọng. Tôi đã trực tiếp thiết kế lại tối ưu trang để đạt 100 điểm Google Lighthouse. Phải lên kế hoạch từ đầu cả thứ tự và cách tải CSS/JS thì về sau mới giảm việc sửa lại. Chúng tôi còn tách CSS trên/dưới fold và inline đúng chỗ; JS dưới fold thì thậm chí không đánh giá cho đến khi người dùng cuộn tới. Chúng tôi áp dụng mọi đề xuất của Lighthouse. Hệ thống cũ thì nạp toàn bộ CSS của cả website cho mọi trang (3~4MB), còn JS thì tệ hơn nữa, vì ngay từ đầu không có thiết kế tối ưu. Chúng tôi vẫn đang dùng hệ thống đó nên không thể nêu tên, nhưng nội bộ vẫn liên tục gặp vấn đề vì nó. Nếu mục tiêu là hiệu năng thì tôi không tin có khái niệm tối ưu hóa quá sớm; phải cân nhắc tất cả ngay từ đầu. Kết quả là chúng tôi đạt hiệu năng 100 điểm cho mọi client, kể cả mobile, và vẫn vượt trội so với đối thủ. Tôi đã thử công cụ này trên site của mình nhưng gần như không còn gì để tối ưu thêm nên không thấy hiệu quả.

  • Với trang của tôi làm bằng framework Astro, HTML là 27.52KB (nén còn 6.1KB), JS dưới 10KB, critical CSS là 57KB (nén còn 7KB). Những site tương tự thường ở mức 100KB~1MB. Nếu làm gọn gàng thì chỉ với resource hints cũng đã đủ nhanh mà không cần inline CSS/JS. Với tổ hợp nginx + HTTP/2 + edge cache thì vẫn có thể đạt 100/100 hiệu năng mà không cần critical CSS hay inline JS. Việc thêm 7KB cho mỗi trang có vẻ không hiệu quả. Về mặt triển khai, SPA/edge caching vừa thân thiện hơn với môi trường vừa nhanh hơn nhiều. Không nhất thiết phải gửi HTML nặng nề quá mức như Elementor. Pin di động là hữu hạn, không có lý do gì phải gửi thêm cả dữ liệu không cần thiết.

    • Đúng là một mẹo hay, nhưng trong bối cảnh CDN và HTTP/2 đã rất phổ biến thì kiểu tối ưu này cuối cùng chỉ lãng phí băng thông. Nó chỉ cải thiện nhẹ các con số benchmark, còn thực tế thì chỉ nhanh hơn cỡ 10~20ms.

    • Không phải lúc nào nhúng critical CSS vào mọi trang cũng là đáp án đúng. Chỉ áp dụng có chọn lọc cho lượt truy cập đầu tiên (khi chưa có cache) hoặc trang mở đầu của một phiên cũng là cách tránh data bloat không cần thiết.

  • Theo yêu cầu của khách hàng, tôi đã tìm công cụ trích xuất critical CSS nhưng không có cái nào đúng tính năng mong muốn, nên cuối cùng chia sẻ giải pháp tự làm bằng Puppeteer. Có thể chỉ định cần chờ bao lâu sau khi trang tải xong. Tôi cũng từng dùng dịch vụ trả phí nhưng không thích nên đã xin hoàn tiền. Rất hoan nghênh phản hồi, và hiện tại tôi mở miễn phí.

    • Không rõ có phải vấn đề nằm ở các công cụ hiện có như gói penthouse hay không.

    • Nếu nhập một website không có CSS thì sẽ báo lỗi.

    • Mã nguồn có được công khai không? Tôi cũng muốn dùng dưới dạng plugin cho Vite/Astro.

    • Tôi muốn hỏi đây có phải là phiên bản có UI của penthouse không, vì khá nhiều giá trị cấu hình trông rất giống.

  • Cách này ngược lại còn phản tác dụng. Nó gây ra FOUC rất nhất quán (nháy trạng thái chưa áp dụng style). Nếu layout thay đổi giữa chừng thì người dùng đang bấm dở thứ gì đó sẽ gặp bất tiện lớn. Đây không chỉ là vấn đề thẩm mỹ mà là vấn đề usability thực sự.

    • Tôi cũng đã chỉnh một số style sau khi áp dụng cách này, nhưng cuối cùng vẫn tối ưu được CLS (cumulative layout shift) về 0. Khi ngân sách ít và phải dùng template có nhiều thư viện thì nó rất hữu ích.

    • Chẳng phải mục tiêu ban đầu là tránh FOUC mà vẫn không block CSS network request sao?

  • Cách này có ý nghĩa hơn trong giả định ở lần xem trang đầu tiên khi hoàn toàn chưa có cache CSS. Trên thực tế còn có nhiều trade-off giữa tỷ lệ người dùng mới/người dùng quay lại, thiết lập cache CSS, CDN, 103 early hints, critical CSS/initial congestion window và nhiều yếu tố khác.

    • Đúng vậy, nó chỉ dành cho lần vào đầu tiên và cũng không hẳn là cách tôi thực sự khuyến nghị, mà chỉ là một trade-off. Cách tốt nhất vẫn là tự viết toàn bộ code và style, đồng thời giảm bớt việc dùng thư viện.
  • Khi đo hiệu năng thì trên localhost gần như không thấy CSS có ảnh hưởng gì đáng kể. Ngay cả khi bỏ hẳn CSS thì mức cải thiện cũng dưới 7ms, mà như vậy vẫn nằm trong sai số đo.

    • Tối ưu hiệu năng latency giữa client-server dĩ nhiên là vô nghĩa trong môi trường độ trễ thấp như localhost. Tôi không cho rằng tối ưu này lúc nào cũng bắt buộc, nhưng test trên localhost không phải benchmark tốt.
  • Tôi dùng công cụ này trên site của mình thì thấy nó còn trích xuất vào CSS cả những thành phần debugging vốn không thực sự cần thiết, ví dụ như body::after dùng cho lớp phủ grid của site cũng bị đưa vào CSS luôn (trước đó tôi đã quên mất, lần này mới phát hiện).

  • Tôi thích cách viết HTML vẫn truyền đạt được ý nghĩa tốt ngay cả khi không có CSS. Làm vậy có thể ngăn tài liệu trở nên quá phức tạp ngay từ sớm.

    • Nhưng điều đó không áp dụng cho mọi website; ví dụ có nhiều UI không lấy cách đọc từ trái sang phải, từ trên xuống dưới làm mặc định.
  • Ý tưởng tự nó khá mới mẻ nên tôi đã thử áp dụng cho trang cá nhân, nhưng thư viện penthouse báo lỗi thiếu CSS: {"error":true,"name":"Error","message":"css should not be empty" ...}

    • Tôi cũng sẽ kiểm tra trường hợp này, cảm ơn.