Tối ưu hoá việc tải và hiển thị WebFont

Ilya Grigorik
Ilya Grigorik

Một WebFont "đầy đủ" bao gồm tất cả các biến thể kiểu chữ mà bạn có thể không cần, cùng với tất cả các ký tự có thể không được sử dụng, dễ dàng dẫn đến việc tải xuống nhiều megabyte. Trong bài đăng này, bạn sẽ tìm hiểu cách tối ưu hoá việc tải WebFonts để khách truy cập chỉ tải những gì họ sẽ sử dụng.

Để giải quyết vấn đề về các tệp lớn chứa tất cả biến thể, quy tắc CSS @font-face được thiết kế riêng để cho phép bạn chia bộ phông chữ thành một tập hợp tài nguyên. Ví dụ: các tập hợp con Unicode và các biến thể kiểu riêng biệt.

Với các nội dung khai báo này, trình duyệt sẽ tìm ra các tập hợp con và biến thể bắt buộc, đồng thời tải tập hợp tối thiểu cần thiết để hiển thị văn bản. Điều này rất thuận tiện. Tuy nhiên, nếu bạn không cẩn thận, việc này cũng có thể tạo ra nút thắt cổ chai về hiệu suất trong đường dẫn kết xuất quan trọng và trì hoãn quá trình kết xuất văn bản.

Hành vi mặc định

Tính năng tải phông chữ từng phần có một hàm ý ẩn quan trọng có thể làm chậm quá trình kết xuất văn bản. Trình duyệt phải xây dựng cây kết xuất, phụ thuộc vào cây DOM và CSSOM, trước khi biết tài nguyên phông chữ nào cần thiết để hiển thị văn bản. Do đó, các yêu cầu phông chữ bị trì hoãn sau các tài nguyên quan trọng khác, và trình duyệt có thể bị chặn hiển thị văn bản cho đến khi tài nguyên được tìm nạp.

Đường dẫn kết xuất quan trọng của phông chữ

  1. Trình duyệt yêu cầu tài liệu HTML.
  2. Trình duyệt bắt đầu phân tích cú pháp phản hồi HTML và tạo DOM.
  3. Trình duyệt sẽ khám phá CSS, JS và các tài nguyên khác, đồng thời gửi yêu cầu.
  4. Trình duyệt tạo CSSOM sau khi nhận được tất cả nội dung CSS và kết hợp nội dung đó với cây DOM để tạo cây kết xuất.
    • Yêu cầu phông chữ được gửi sau khi cây kết xuất cho biết cần có biến thể phông chữ nào để kết xuất văn bản đã chỉ định trên trang.
  5. Trình duyệt thực hiện bố cục và vẽ nội dung lên màn hình.
    • Nếu phông chữ chưa có sẵn, trình duyệt có thể không hiển thị bất kỳ pixel văn bản nào.
    • Sau khi có phông chữ, trình duyệt sẽ vẽ các pixel văn bản.

"Cuộc đua" giữa lần vẽ đầu tiên của nội dung trang, có thể được thực hiện ngay sau khi tạo cây kết xuất, và yêu cầu tài nguyên phông chữ là nguyên nhân tạo ra "vấn đề văn bản trống" trong đó trình duyệt có thể kết xuất bố cục trang nhưng bỏ qua mọi văn bản.

Bằng cách tải trước WebFonts và sử dụng font-display để kiểm soát cách trình duyệt hoạt động với các phông chữ không có sẵn, bạn có thể ngăn các trang trống và bố cục thay đổi do quá trình tải phông chữ.

Tải trước tài nguyên WebFont

Nếu có nhiều khả năng trang của bạn sẽ cần một WebFont cụ thể được lưu trữ tại một URL mà bạn biết trước, thì bạn có thể tận dụng tính năng ưu tiên tài nguyên. Việc sử dụng <link rel="preload"> sẽ kích hoạt yêu cầu cho WebFont sớm trong đường dẫn kết xuất quan trọng, mà không cần phải chờ CSSOM được tạo.

Tuỳ chỉnh độ trễ hiển thị văn bản

Mặc dù việc tải trước giúp tăng khả năng WebFont sẽ có sẵn khi nội dung của trang được hiển thị, nhưng điều này không đảm bảo. Bạn vẫn cần cân nhắc cách trình duyệt hoạt động khi hiển thị văn bản sử dụng font-family chưa có.

Trong bài đăng Tránh văn bản không hiển thị trong khi tải phông chữ, bạn có thể thấy rằng hành vi mặc định của trình duyệt không nhất quán. Tuy nhiên, bạn có thể cho trình duyệt hiện đại biết cách bạn muốn trình duyệt hoạt động bằng cách sử dụng font-display.

Hỗ trợ trình duyệt

  • Chrome: 60.
  • Edge: 79.
  • Firefox: 58.
  • Safari: 11.1.

Nguồn

Tương tự như các hành vi hết thời gian chờ phông chữ hiện có mà một số trình duyệt triển khai, font-display phân đoạn thời gian tải phông chữ xuống thành ba giai đoạn chính:

  1. Khoảng thời gian đầu tiên là khoảng thời gian khối phông chữ. Trong khoảng thời gian này, nếu phông chữ không được tải, thì mọi phần tử cố gắng sử dụng phông chữ đó phải hiển thị bằng phông chữ dự phòng không hiển thị. Nếu phông chữ tải thành công trong khoảng thời gian chặn, thì phông chữ đó sẽ được sử dụng bình thường.
  2. Thời gian hoán đổi phông chữ xảy ra ngay sau thời gian chặn phông chữ. Trong khoảng thời gian này, nếu phông chữ không được tải, thì mọi phần tử cố gắng sử dụng phông chữ đó phải hiển thị bằng phông chữ dự phòng. Nếu phông chữ tải thành công trong khoảng thời gian hoán đổi, thì phông chữ đó sẽ được sử dụng bình thường.
  3. Khoảng thời gian lỗi phông chữ xảy ra ngay sau khoảng thời gian hoán đổi phông chữ. Nếu mặt phông chữ chưa được tải khi khoảng thời gian này bắt đầu, thì mặt phông chữ sẽ được đánh dấu là tải không thành công, dẫn đến việc sử dụng phông chữ dự phòng thông thường. Nếu không, phông chữ sẽ được sử dụng bình thường.

Khi hiểu rõ các khoảng thời gian này, bạn có thể sử dụng font-display để quyết định cách hiển thị phông chữ tuỳ thuộc vào việc phông chữ đó đã được tải xuống hay chưa và thời điểm tải xuống.

Để xử lý thuộc tính font-display, hãy thêm thuộc tính này vào quy tắc @font-face:

@font-face {
  font-family: 'Awesome Font';
  font-style: normal;
  font-weight: 400;
  font-display: auto; /* or block, swap, fallback, optional */
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range: U+000-5FF; /* Latin glyphs */
}

font-display hiện hỗ trợ dải giá trị sau:

  • auto
  • block
  • swap
  • fallback
  • optional

Để biết thêm thông tin về cách tải trước phông chữ và thuộc tính font-display, hãy xem các bài đăng sau:

API Tải phông chữ

Khi được sử dụng cùng nhau, <link rel="preload"> và CSS font-display sẽ giúp bạn kiểm soát rất nhiều việc tải và kết xuất phông chữ mà không làm tăng thêm nhiều chi phí. Tuy nhiên, nếu bạn cần tuỳ chỉnh thêm và sẵn sàng chịu chi phí phát sinh khi chạy JavaScript, thì bạn có một lựa chọn khác.

Font Loading API (API Tải phông chữ) cung cấp một giao diện tập lệnh để xác định và thao tác với các phông chữ CSS, theo dõi tiến trình tải xuống và ghi đè hành vi tải lười mặc định của các phông chữ đó. Ví dụ: nếu chắc chắn rằng cần có một biến thể phông chữ cụ thể, bạn có thể xác định biến thể đó và yêu cầu trình duyệt bắt đầu tìm nạp tài nguyên phông chữ ngay lập tức:

Hỗ trợ trình duyệt

  • Chrome: 35.
  • Edge: 79.
  • Firefox: 41.
  • Safari: 10.

Nguồn

var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
  style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
});

// don't wait for the render tree, initiate an immediate fetch!
font.load().then(function() {
  // apply the font (which may re-render text and cause a page reflow)
  // after the font has finished downloading
  document.fonts.add(font);
  document.body.style.fontFamily = "Awesome Font, serif";

  // OR... by default the content is hidden,
  // and it's rendered after the font is available
  var content = document.getElementById("content");
  content.style.visibility = "visible";

  // OR... apply your own render strategy here...
});

Ngoài ra, vì bạn có thể kiểm tra trạng thái phông chữ (thông qua phương thức check()) và theo dõi tiến trình tải xuống, nên bạn cũng có thể xác định chiến lược tuỳ chỉnh để hiển thị văn bản trên các trang của mình:

  • Bạn có thể giữ tất cả hoạt động kết xuất văn bản cho đến khi phông chữ có sẵn.
  • Bạn có thể triển khai thời gian chờ tuỳ chỉnh cho từng phông chữ.
  • Bạn có thể sử dụng phông chữ dự phòng để bỏ chặn tính năng kết xuất và chèn một kiểu mới sử dụng phông chữ mong muốn sau khi phông chữ đó có sẵn.

Quan trọng nhất là bạn cũng có thể kết hợp các chiến lược trên cho nhiều nội dung trên trang. Ví dụ: bạn có thể trì hoãn việc kết xuất văn bản trên một số phần cho đến khi có phông chữ, sử dụng phông chữ dự phòng, sau đó kết xuất lại sau khi quá trình tải phông chữ xuống hoàn tất.

Phải lưu vào bộ nhớ đệm đúng cách

Tài nguyên phông chữ thường là tài nguyên tĩnh không được cập nhật thường xuyên. Do đó, các tệp này rất phù hợp với thời gian hết hạn tối đa dài hạn – hãy đảm bảo rằng bạn chỉ định cả tiêu đề ETag có điều kiệnchính sách tối ưu về Kiểm soát bộ nhớ đệm cho tất cả tài nguyên phông chữ.

Nếu ứng dụng web của bạn sử dụng trình chạy dịch vụ, thì việc phân phát tài nguyên phông chữ bằng chiến lược ưu tiên bộ nhớ đệm sẽ phù hợp với hầu hết các trường hợp sử dụng.

Bạn không nên lưu trữ phông chữ bằng localStorage hoặc IndexedDB; mỗi phương thức đều có một bộ vấn đề về hiệu suất riêng. Bộ nhớ đệm HTTP của trình duyệt cung cấp cơ chế tốt nhất và mạnh mẽ nhất để phân phối tài nguyên phông chữ cho trình duyệt.

Danh sách kiểm tra việc tải WebFont

  • Tuỳ chỉnh tính năng tải và kết xuất phông chữ bằng <link rel="preload">, font-display hoặc API tải phông chữ: hành vi tải từng phần mặc định có thể dẫn đến việc kết xuất văn bản bị chậm trễ. Các tính năng nền tảng web này cho phép bạn ghi đè hành vi này cho các phông chữ cụ thể, đồng thời chỉ định các chiến lược kết xuất tuỳ chỉnh và thời gian chờ cho nhiều nội dung trên trang.
  • Chỉ định chính sách xác thực lại và lưu vào bộ nhớ đệm tối ưu: phông chữ là tài nguyên tĩnh được cập nhật không thường xuyên. Đảm bảo rằng máy chủ của bạn cung cấp dấu thời gian max-age có thời gian tồn tại lâu dài và mã thông báo xác thực lại để cho phép sử dụng lại phông chữ hiệu quả giữa các trang. Nếu sử dụng worker dịch vụ, bạn nên dùng chiến lược ưu tiên bộ nhớ đệm.

Kiểm thử tự động hành vi tải WebFont bằng Lighthouse

Lighthouse có thể giúp tự động hoá quy trình đảm bảo rằng bạn đang làm theo các phương pháp hay nhất để tối ưu hoá phông chữ trên web.

Các quy trình kiểm tra sau đây có thể giúp bạn đảm bảo rằng các trang của mình tiếp tục tuân thủ các phương pháp hay nhất về việc tối ưu hoá phông chữ web theo thời gian: