Preload các tài nguyên quan trọng để tăng tốc độ website

Khi bạn mở một trang web, trình duyệt sẽ gửi các yêu cầu tài liệu HTML đến máy chủ (server), phân tích nội dung của nó, và gửi các yêu cầu riêng cho bất kỳ nguồn tài nguyên nào được tham chiếu đến (referenced resource). Là người lập trình, bạn cần biết về tất cả các nguồn tài nguyên mà trang của bạn cần và cái nào trong số đó là quan trọng nhất (most important). Bạn có thể sử dụng hiểu biết đó để yêu cầu các tài nguyên quan trọng (critical resources) trước và qua đó giúp tăng tốc quá trình tải. Bài viết này sẽ giải thích cách để làm điều đó với thẻ <link rel=”preload”>.

Cách preload làm việc

Preload phù hợp nhất với các kiểu tài nguyên quan trọng nhưng lại thường được trình duyệt phát hiện muộn.

waterfall của trang
Trong ví dụ này, font Pacifico được định nghĩa trong CSS với quy tắc @font-face. Do vậy, thông thường trình duyệt chỉ tải được file font sau khi nó đã tải và phân tích xong CSS

Bằng cách preload một tài nguyên nhất định, bạn nói với trình duyệt rằng bạn muốn tìm nạp nó sớm hơn so với để trình duyệt tự phát hiện ra tài nguyên đấy theo mặc định thông thường, bởi vì bạn chắc chắn rằng nó quan trọng cho trang hiện đang được duyệt.

waterfall sau đó
Trong ví dụ này, font Pacifico được preload (tải trước), vì thế việc tải font diễn ra song song (parallel) với tải CSS (stylesheet). Font tùy chỉnh được tải trước giúp tránh hiện tượng FOIT, để đảm bảo văn bản vẫn hiển thị trong khi font tải về

Chuỗi yêu cầu quan trọng thể hiện thứ tự của các tài nguyên được trình duyệt ưu tiên và tìm nạp. Lighthouse xác định các tài nguyên ở cấp độ thứ ba của chuỗi này là được phát hiện muộn. Bạn có thể sử dụng trình kiểm tra Preload key requests để xác định tài nguyên nào nên preload.

Các yêu cầu preload

Bạn có thể preload các tài nguyên bằng cách thêm thẻ <link> đi kèm rel=’preload’ vào phần <head> của tài liệu HTML, ví dụ:

<link rel="preload" as="script" href="critical.js">

Trình duyệt sẽ cache các tài nguyên đã được preload, vì thế chúng có khả năng được cung cấp ngay lập tức khi trình duyệt cần đến (nói cách khác, nó chỉ tải xuống, và vẫn chưa thực thi mã JS hoặc áp dụng CSS sau khi tải xong, điều này rất quan trọng để đảm bảo website hoạt động đúng như mong muốn).

Sau khi triển khai preload, nhiều trang, bao gồm Shopfy, Finacial Time và Treebo nhận thấy trang của họ cải thiện 1s dưới khía cạnh các chỉ số tốc độ tập trung vào người dùng cuối, chẳng hạn như thời gian tương tác (Time To Interactive) và thời gian thấy nội dung đầu tiên trên trang (First Contentful Paint).

Các gợi ý tài nguyên khác, chẳng hạn như preconnectprefetch được thực thi khi trình duyệt thấy phù hợp. Ngược lại preload là chỉ thị bắt buộc (mandatory) đối với trình duyệt. Các trình duyệt hiện đại thực hiện khá tốt nhiệm vụ ưu tiên các tài nguyên, đó là lý do vì sao bạn phải sử dụng preload hết sức thận trọng, chỉ nên preload các tài nguyên quan trọng nhất mà thôi.

Các tài nguyên được preload nhưng lại không được sử dụng sẽ gửi đi cảnh báo trong Chrome, khoảng thời gian để nó xét tài nguyên có được dùng hay không là khoảng 3 giây sau khi sự kiện load được kích hoạt.

Đọc thêm:  Prefetch là gì? Tìm nạp trước tài nguyên để tăng tốc các truy cập kế tiếp của người dùng
Cảnh báo của console
Thông báo ở trên nói rằng: Tài nguyên 1tcvlsq đã được gắn thuộc tính preload nhưng lại không được sử dụng vài giây sau khi nó được kích hoạt tải xuống. Hãy đảm bảo là bạn không preload các tài nguyên không được dùng.

Lưu ý: Preload được hỗ trợ trên tất cả các trình duyệt hiện đại, ngoại trừ FireFox.

Các ứng dụng thực tế

Preload các tài nguyên được định nghĩa bên trong CSS

Các font được định nghĩa bằng quy tắc @font-face hoặc các ảnh nền (background) được định nghĩa trong file CSS sẽ không được trình duyệt biết tới cho đến khi trình duyệt tải và phân tích xong các file CSS. Preload các tài nguyên này đảm bảo chúng được tìm nạp trước khi các file CSS tải xong.

Preload các file CSS

Nếu bạn đang áp dụng cách tiếp cận dùng critical CSS, tức là bạn chia CSS của trang thành hai phần. Critical CSS được dùng để render nội dung thuộc về màn hình đầu tiên, nó được inline (nội tuyến) vào trong thẻ <head> của tài liệu, còn non-critical CSS thường được lazy-load bằng JavaScript. Việc phải đợi JavaScript thực thi trước khi tải về CSS không quan trọng (non-critical) có thể là nguyên nhân gây trì hoãn việc render khi người dùng cuộn chuột, vì thế ý tưởng tốt là sử dụng <link rel=”preload”> để tải nó xuống sớm hơn.

Một file CSS trên Kiến càng được preload
Một file CSS trên Kiến càng được preload

Preload các file JavaScript

Do các trình duyệt không thực thi các tệp được preload (nó chỉ tải trước về và đưa vào trong cache), nên preload rất hữu ích trong việc tách tìm nạp ra khỏi thực thi, điều này có thể cải thiện các số liệu như Time To Interactive (Thời gian cho phép tương tác). Preload hoạt động tốt nhất khi bạn chia JavaScript thành nhiều tệp, và chỉ preload các tệp quan trọng.

Cách triển khai rel=preload

Cách đơn giản nhất để triển khai preload là thêm thẻ <link> vào phần <head> của tài liệu:

<head>
  <link rel="preload" as="script" href="critical.js">
</head>

Việc cung cấp thêm thuộc tính as giúp trình duyệt thiết lập ưu tiên cho tài nguyên được tìm nạp dựa trên kiểu của nó, thiết lập đúng các header, và phát hiện liệu tài nguyên đó đã có sẵn trong cache hay là chưa? Các giá trị được chấp nhận cho thuộc tính này bao gồm: script, style, font, image, và một số cái khác.

Bạn có thể tham khảo tài liệu về ưu tiên và lên lịch các nguồn tài nguyên trong Chrome để biết cách trình duyệt thiết lập ưu tiên cho các kiểu tài nguyên khác nhau như thế nào.

Lưu ý: Việc bỏ qua thuộc tính as, hoặc có giá trị không hợp lệ tương đương với yêu cầu XHR, làm cho trình duyệt không biết nội dung gì được tìm nạp, vì thế nó không thể xác định mức độ ưu tiên chính xác được. Nó cũng có thể là nguyên nhân khiến một số tài nguyên, chẳng hạn như script bị tìm nạp đến hai lần.

Một số kiểu tài nguyên, chẳng hạn như font, được tải trong chế độ anonymous (vô danh). Với những kiểu tài nguyên như vậy, bạn phải thiết lập thuộc tính crossorigin kèm theo preload:

<link rel="preload" href="ComicSans.woff2" as="font" type="font/woff2" crossorigin>

Chú ý: Preload font nếu không có thuộc tính crossorigin sẽ phải tìm nạp hai lần!

Đọc thêm:  Time To Interactive (TTI) là gì?

Phần tử <link> cũng chấp nhận thuộc tính type, cái bao gồm MIME type của tài nguyên được liên kết. Các trình duyệt sử dụng giá trị của thuộc tính type để đảm bảo rằng các tài nguyên được preload chỉ khi kiểu file của chúng được hỗ trợ. Nếu trình duyệt không hỗ trợ một kiểu tài nguyên cụ thể nào đó, nó sẽ bỏ qua thẻ <link rel=”preload”>.

Bạn cũng có thể preload bất cứ kiểu tài nguyên nào thông qua Link HTTP header:

Link: </css/style.css>; rel="preload"; as="style"

Một lợi thế của việc chỉ định preload thông qua HTTP Header là trình duyệt không cần phải phân tích tài liệu để khám phá ra nó, điều đó có thể giúp bạn có được cải thiện nhỏ trong một số trường hợp.

Preload module JavaScript bằng webpack

Nếu bạn sử dụng module bundler cho việc xây dựng các tệp của ứng dụng, bạn cần kiểm tra xem nó có hỗ trợ tính năng injection của thẻ preload hay không. Với webpack từ phiên bản 4.6.0 trở đi, preload được hỗ trợ thông qua sử dụng magic comments bên trong import():

import(_/* webpackPreload: true */_ "CriticalChunk")

Nếu bạn sử dụng phiên bản webpack cũ hơn, bạn cần sử dụng plugin của bên thứ ba có tên preload-webpack-plugin.

Kết luận

Để cải thiện tốc độ tải trang, bạn nên preload các tài nguyên quan trọng mà nếu theo cách thông thường nó sẽ bị phát hiện muộn trong trình duyệt (có thể vì nó ẩn trong các file CSS, hoặc nó có vị trí thấp trong file HTML, vân vân). Preload mọi thứ sẽ phản tác dụng do vậy bạn phải dùng preload một cách cẩn trọng và đo đạc ảnh hưởng của nó trong thế giới thực (real-world).

Ví dụ demo

Phần này tôi (người dịch) thêm vào để chúng ta dễ hiểu, và dễ kiểm tra hơn. Tôi tạo 2 trang có nội dung giống hệt nhau, chỉ khác là một trang có preload, còn một trang thì không.

Mô hình thế này: tôi tạo trang với hai font tùy chỉnh và một ảnh nền làm background, trong đó cả hai tài nguyên này đều nằm trong CSS. Điều đó có nghĩa là, theo mặc định, trình duyệt sẽ phải tải CSS và phân tích cú pháp xong thì mới tải font tùy chỉnh và ảnh background được. Trang thông thường sẽ giữ nguyên cấu trúc như vừa mô tả, còn với trang có preload, tôi chủ động preload cả ba tài nguyên (gồm hai font và một ảnh) để xem sự khác biệt giữa chúng như thế nào.

Trang tải theo mặc định: https://code.speed.family/normal-font-bg.html

Trang preload trước tài nguyên: https://code.speed.family/preload-font-bg.html

Đoạn mã mẫu preload (khi triển khai bạn nên để ý đến tất cả các thuộc tính là rel, href, as, typecrossorigin):

<link rel="preload" href="fonts/thu-phap.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="fonts/tap-viet.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="https://speed.cdn.vccloud.vn/wp-content/uploads/2020/03/video-lazy-load.png" as="image" type="image/png">
<link rel='stylesheet' href='https://code.speed.family/css/normal-font-bg.css' type='text/css' media='all' />

Kết quả kiểm tra với DevTools (Lighthouse) trên Chrome:

Đọc thêm:  Sử dụng preconnect để gửi yêu cầu trước cho server gốc bên ngoài
trang tải thông thường
Trên trang tải thông thường, bạn có thể thấy file CSS phải tải và phân tích xong thì hai font và ảnh nền mới tải về
trang có preload
Trên trang có preload, bạn thấy cả 4 tài nguyên là hai font, css và ảnh nền cùng tải về cùng một lúc

Vì 2 trang giống y nhau (ngoại trừ khác biệt về preload) nên số lượng request (6), dung lượng tải về (180/240) đều giống nhau. Điểm khác biệt dễ thấy là thời gian tải về (Finish) của trang có preload ít hơn (nghĩa là tốt hơn, chỉ 1,80s so với 2,24s, mỗi lần kiểm tra con số này sẽ thay đổi do nó còn phụ thuộc vào điều kiện kết nối mạng, nhưng nhìn chung trang có preload sẽ tải nhanh hơn).

Tại sao trang có preload tải nhanh hơn: Hãy tưởng tượng cách tải website giống như xây một căn nhà, CSS chính bản thiết kế bạn thuê kiến trúc sư vẽ. Còn ảnh, font giống như nguyên vật liệu xây nên căn nhà. Rất hợp lý khi chúng ta cần có bản thiết kế trong tay thì mới biết nên mua nguyên vật liệu nào, số lượng bao nhiêu, nhưng thường bạn đã nắm rõ các vật liệu căn bản cần xây căn nhà rồi. Như vậy sẽ nhanh hơn nếu đồng thời, vào khoảng thời gian chờ đợi bản thiết kế được chuyển từ Sài Gòn về Hà Nội (hỏa tốc thì cũng phải mất 1 ngày), thì lúc ấy bạn cũng nhập luôn vật liệu cần thiết như xi măng, đá, mạt, cát, sắt thép. Thời gian tiết kiệm được là nhờ bạn không phải chờ đợi bản thiết kế về rồi mới mua vật liệu (mất thêm ít nhất một ngày). Khi vật liệu có sẵn, và bản thiết kế về, bạn bắt tay vào xây nhà luôn.

Có thể có một sai lầm nhỏ trong thứ tự preload, ban đầu nó thế này:

thứ tự preload
file CSS được tôi để ở dưới cùng

Theo thiết kế của trang thì font thu-phap.ttf và ảnh background nằm trong màn hình đầu tiên, trong khi font tap-viet.ttf dùng trong màn hình thứ hai, nên đáng ra tap-viet.ttf sẽ phải ở dưới cùng. Ngoài ra, tôi thử để file CSS lên trên cùng xem thứ tự tải có thay đổi gì không, mã thay đổi như sau:

<link rel='stylesheet' href='https://code.speed.family/css/normal-font-bg.css' type='text/css' media='all' />
<link rel="preload" href="fonts/thu-phap.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="https://speed.cdn.vccloud.vn/wp-content/uploads/2020/03/video-lazy-load.png" as="image" type="image/png">
<link rel="preload" href="fonts/tap-viet.ttf" as="font" type="font/ttf" crossorigin>

Trang demo: https://code.speed.family/thu-tu-preload1.html

Kết quả là thứ tự tải về tài nguyên đã thay đổi theo chỉ dẫn, nhưng file CSS vẫn tải về cuối cùng (điều này làm chúng ta nhớ lại rằng chỉ thị preload là chỉ thị bắt buộc, và trình duyệt tuân thủ điều này bằng cách tải các tài nguyên preload trước ngay cả khi CSS được đặt ở trên cùng trong tài liệu HTML):

thứ tự tài nguyên tải về đã thay đổi, nhưng file CSS vẫn ở cuối

Tôi nghĩ để file CSS ở cuối sẽ hợp lý hơn, vì dù sao phải đủ nguyên vật liệu thì mới xây nhà dựa trên bản thiết kế được, ngược lại có bản thiết kế mà chưa có nguyên vật liệu thì bạn cũng chưa thể làm gì cả.

(Dịch từ bài viết Preload critical assets to improve loading speed, tác giả: Houssein Djirdeh và Milica Mihajlija, website: web[.]dev)

6 thoughts on “Preload các tài nguyên quan trọng để tăng tốc độ website”

  1. em có Website baychim.com có thuộc tính:
    …icons/fl-icons.woff2
    Giờ viết câu lệnh như nào cho đúng ạ

    Reply
      • Trên mấy IOS, Androi cũ nó không chạy nên lại chuyển sang cách khác trong bài. Tuy không đạt 98-100 nhưng cũng ổn.
        PS: Đang khi gõ những dòng này mình nghĩ lại, vì trên IOS cũ font chữ mặc định cũng đẹp. Chắc suy nghĩ thêm. Đâu đầu ghê

        Reply

Leave a Comment