Trước khi tìm hiểu về defer và async trong thẻ <script>
, chúng ta cùng quay ngược thời gian để xem cách thức hoạt động của thẻ <script>
thông thường nhé.
Hẳn bạn đã từng sử dụng cú pháp sau đây để nhúng một file JavaScript vào trang web:
`<script src="/script.js"></script>`
Cách làm này vẫn rất phổ biến cho tới tận bây giờ. Tuy nhiên, nó lại gây ra một vấn đề không nhỏ với việc tối ưu hóa tải trang web.
--──────────────────────┬───────────────────────────────────────┬───────────────────┐
/ Parse the document / Pause parsing / Resume parsing /
└───────────────────────┼───────────────────┬───────────────────┴───────────────────┘ / Download script / └───────────────────┼───────────────────┐ / Execute script / └───────────────────┘
Giải thích:
- Khi trình duyệt gặp thẻ
<script>
, nó sẽ tạm dừng việc parse (phân tích cú pháp) tài liệu HTML. - Sau đó, trình duyệt sẽ bắt đầu tải xuống file JavaScript được chỉ định trong thẻ
<script>
. - Khi file JavaScript đã được tải xuống hoàn tất, trình duyệt sẽ thực thi nội dung của file đó.
- Sau khi thực thi xong JavaScript, trình duyệt mới có thể tiếp tục công việc parse tài liệu HTML bị tạm dừng lại.
=> Như vậy, việc sử dụng thẻ<script>
thông thường sẽ làm chậm quá trình parse và render trang web, vì trình duyệt phải dừng việc parse HTML để tải và thực thi JavaScript. Đây chính là vấn đề mà defer và async giải quyết.
Async là gì?
<script src="/script.js" async></script>
Thuộc tính async cho phép tải script độc lập với việc parse HTML. Khi gặp thẻ <script async>
, trình duyệt sẽ tải xuống script song song với việc parse phần còn lại của trang. Sau khi tải xong, script sẽ được thực thi ngay lập tức.
┌───────────────────────────────────────────┬───────────────────┬───────────────────┐
/ Parse the document / Pause parsing / Resume parsing /
└───────────────────────┬───────────────────┼───────────────────┴───────────────────┘ / Download script / └───────────────────┼───────────────────┐ / Execute script / └───────────────────┘
Đây là cách giải thích luồng hoạt động của Async:
- Khi trình duyệt gặp thẻ script có async, nó sẽ tiếp tục parse phần tài liệu phía sau thẻ script thay vì dừng lại để chạy script như bình thường..
- Trình duyệt sẽ tải về file script song song với việc parse tài liệu.
- Khi file script đã tải xong, trình duyệt sẽ tạm dừng việc parse tài liệu để thực thi script.
- Sau khi script thực thi xong, trình duyệt sẽ tiếp tục parse phần tài liệu còn lại.
Defer là gì?
<script src="/script.js" defer></script>
Thuộc tính Defer cho phép tải script độc lập với HTML, nhưng sẽ chỉ thực thi script sau khi toàn bộ tài liệu đã được parse xong.
───────────────────────────────────────────────────────────┐
/ Parse the document /
└───────────────────────┬─────────────────┬─────────────────┘ / Download script / └─────────────────┘ ┌───────────────────┐ / Execute script / └───────────────────┘
Giải thích:
- Khi trình duyệt gặp thẻ script có defer vẫn sẽ tiếp tục parse HTML, không dừng lại để chạy script.
- Tải xuống script được chỉ định trong thẻ script defer đồng thời với việc parse HTML.
- Sau khi parse xong HTML, mới chạy các script đã tải về.
- Các script chạy trước khi sự kiện DOMContentLoaded được kích hoạt.
Vị trí đặt script defer, async
Vị trí đặt thuộc tính defer và async trong thẻ <script>
nên tuân theo các nguyên tắc sau:
- Đặt ở phía trên cùng của thẻ
<body>
, ngay sau thẻ<head>
để tải xuống càng sớm càng tốt. - Đặt trước các script không async hoặc defer.
- Đặt sau thư viện và các script cần được thực thi trước.
- Không nên đặt giữa các thẻ HTML vì sẽ làm chậm quá trình render.
- Đặt các script liên quan đến CSS ở cuối
<head>
. - Đặt các script cần dữ liệu DOM ở cuối
<body>
. - Nên nhóm các script cùng loại lại gần nhau để dễ quản lý.
Như vậy, vị trí lý tưởng là ngay đầu
<body>
đối với defer và async, cuối<head>
đối với các script liên quan CSS.
Khi nào nên sử dụng script defer, async
- Sử dụng defer cho các script không cần thiết phải chạy ngay lập tức, nhưng vẫn cần chạy theo đúng thứ tự. Điều này giúp tăng tốc độ tải trang.
- Sử dụng async cho các script độc lập, không phụ thuộc vào các script khác. Ví dụ Google Analytics, quảng cáo...
- Không nên async cho các script phụ thuộc vào DOM hoặc các script khác.
- Sử dụng defer cho thư viện JS được sử dụng ở nhiều trang. Giúp cache tốt hơn.
- Sử dụng defer cho các script dùng chung trên toàn website.
- Không sử dụng defer async cho các script cần tương tác với người dùng ngay lập tức.
- Đối với trang chỉ có 1 script chính, không cần async hay defer.