banner image

Scripts: async, defer

Trong các website hiện đại, scripts thường "nặng" hơn HTML: kích thước file lớn hơn và quá trình xử lí cũng lâu hơn.

Khi trình duyệt load HTML và đến thẻ <script>...</script>, nó sẽ không tiếp tục xây dựng cấu trúc DOM. Nó phải thực thi các đoạn lệnh script. Điều tương tự với script bên ngoài <script src="...">...</script>, trình duyệt sẽ phải đợi cho đến script được tải về, thực thi code và tiếp tục tiến trình với phần còn lại của trang.
Điều đó dẫn tới hai vấn đề quan trọng:
1. Script không thấy các phần tử DOM element bên dưới chúng vì vậy không thể thêm các sự kiện để xử lí.
2. Nếu có một đoạn script cồng kềnh ở đầu trang thì nó sẽ chặn trang. Người dùng không thể xem nội dung trang web cho đến khi tải xong và thực thi code.

<p>...content before script...</p>

<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- This isn't visible until the script loads -->
<p>...content after script...</p>
Ở ví dụ trên chúng ta sẽ thấy ...content before script... hiển thị trước sau đó phải đợi script load về và thực thi xong thì mới xuất hiện ...content after script...
Có một số cách giải quyết vấn đề. Chẳng hạn, chúng ta có thể đặt một đoạn script ở cuối trang. Sau đó, chúng ta có thể thấy các phần tử bên trên nó và nó không chặn nội dung hiển thị:

<body>
  ...all content is above the script...

  <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>
Nhưng giải pháp này không hoàn hảo lắm. Ví dụ: trình duyệt chú ý tới các script (và có thể bắt đầu tải xuống) chỉ sau khi nó tải xong toàn bộ HTML đầy đủ.

Đối với các trang HTML dài có thể có một độ trễ nhỏ. Những thứ như vậy là vô hình đối với những người sử dụng kết nối nhanh, nhưng nhiều người trên thế giới vẫn còn tốc độ internet miền núi hoặc sử dụng kết nối wifi rùa bò. May mắn thay, có hai thuộc tính của <script> giải quyết vấn đề này: deferasync

1. Defer

Thuộc tính defer báo cho trình duyệt biết rằng nó sẽ tiếp tục tải trang và tải tập lệnh script song song, sau đó chạy script khi tải xong. Ở đây, ví dụ tương tự như trên, nhưng với defer:

<p>...content before script...</p>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- Visible immediately -->
<p>...content after script...</p>
Ở ví dụ trên chúng ta sẽ thấy ...content before script......content after script... hiển thị cùng lúc sau đó script load về và thực thi.
Script với defer sẽ không ngăn quá trình tải trang.
Script với defer luôn luôn thực thi khi DOM đã sẵn sàng, nhưng trước sự kiện DOMContentLoaded.

<p>...content before scripts...</p>

<script>
  document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!")); // (2)
</script>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<p>...content after scripts...</p>
Ở ví dụ trên chúng ta sẽ thấy ...content before script......content after script... hiển thị cùng lúc.
Sau đó là script với defer.
Sau đó là alert("DOM ready after defer!").
1. Nội dung trang hiện lên ngay lập tức.
2. DOMContentLoaded sẽ chờ đoạn script với defer. Nó chỉ kích hoạt khi tập lệnh (2) được tải xuống và thực thi.

Các thẻ script với defer vẫn tuân theo trật tự của chúng, giống như các tập lệnh thông thường. Vì vậy, nếu chúng ta có một script dài trước, và sau đó là một script ngắn hơn, thì script sau sẽ chờ.

<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>
Script nhỏ tải xuống trước, chạy thứ hai. Trình duyệt quét trang tìm các script và tải chúng song song để cải thiện hiệu suất. Vì vậy, trong ví dụ trên cả hai script tải xuống song song small.js có thể làm cho nó đầu tiên. Nhưng đặc tả yêu cầu các tập lệnh thực thi theo thứ tự tài liệu, vì vậy nó chờ cho long.js thực thi.
Thuộc tính defer chỉ dành cho các script bên ngoài
Thuộc tính defer bị bỏ qua nếu thẻ <script> không có src.

2. async

Thuộc tính async có nghĩa là một tập lệnh hoàn toàn độc lập: Trang web không chờ đợi các file script async, nội dung tiếp tục được xử lý và hiển thị. DOMContentLoaded và script async không chờ đợi nhau: DOMContentLoaded có thể xảy ra trước cả script async (nếu script async kết thúc tải sau khi trang hoàn tất) Hoặc sau một file script async (nếu script async ngắn hoặc nằm trong bộ đệm HTTP) Các file script khác không đợi file script async và tương tự các file script async cũng không đợi chúng. Vì vậy, nếu chúng ta có một vài file script async, chúng có thể thực thi theo bất kỳ thứ tự nào. Bất cứ thứ gì tải trước - chạy trước:

<p>...content before scripts...</p>

<script>
  document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>

<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>

<p>...content after scripts...</p>
1. Nội dung trang hiện lên ngay lập tức: thuộc tính async không chặn trang. 2. DOMContentLoaded có thể xảy ra cả trước hoặc sau async, không có đảm bảo nào ở đây. 3. Các file script async không chờ nhau. Một tập lệnh nhỏ hơn small.js đi thứ hai, nhưng có thể tải trước long.js, vì vậy chạy trước. Điều đó được gọi là một thứ tự tải đầu tiên. Scripts async rất tuyệt vời khi chúng ta tích hợp script của bên thứ ba độc lập vào trang web: bộ đếm, quảng cáo, v.v., vì chúng không phụ thuộc vào script của chúng ta và script của chúng ta cũng không nên chờ chúng:

<!-- Google Analytics is usually added like this -->
<script async src="https://google-analytics.com/analytics.js"></script>

3. Dynamic scripts


let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)
Chúng ta cũng có thể thêm một Dynamic script bằng cách sử dụng JavaScript. Các script bắt đầu tải ngay sau khi nó được gắn vào tài liệu (*).

Theo mặc định, các tập lệnh Dynamic script hoạt động như là script async.


let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";

script.async = false;

document.body.append(script);
1. Chúng không chờ đợi bất cứ ai và không ai chờ đợi chúng.
2. Script nào tải trước - chạy trước (Tuân theo thứ tự tải).


function loadScript(src) {
  let script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.body.append(script);
}

// long.js runs first because of async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");
Ví dụ ở trên, ở đây chúng ta thêm hai file script. Nếu không có async = false, chúng sẽ thực thi theo thứ tự tải đầu tiên (có thể là small.js trước). Nhưng với việc đặt async = false thứ tự là như trong code.

4. Tổng kết

Cả asyncdefer đều có một điểm chung: việc tải xuống các file script như vậy không ngăn chặn việc hiển thị trang. Vì vậy, người dùng có thể đọc nội dung trang và làm quen với trang ngay lập tức. Nhưng cũng có những khác biệt cần thiết giữa chúng:
Thứ tự DOMContentLoaded
async Thứ tự theo tải đầu tiên. Thứ tự trong HTML không thành vấn đề - tải trước - Không liên quan. Có thể tải và thực thi trong khi script chưa được tải xuống đầy đủ. Điều đó xảy ra nếu các script nhỏ hoặc đã được lưu trữ và tài liệu đủ dài.
defer Theo thứ tự document HTML Thực thi sau khi script được tải và phân tích cú pháp (họ chờ nếu cần), ngay trước DOMContentLoaded.

Lưu ý

Xin lưu ý rằng nếu bạn sử dụng defer, thì trang sẽ hiển thị trước khi tải tập lệnh. Vì vậy, người dùng có thể đọc trang này, nhưng một số thành phần đồ họa có thể chưa sẵn sàng.
Cần có các chỉ dẫn tải xuống ở các vị trí thích hợp và các nút bị vô hiệu hóa sẽ hiển thị để người dùng có thể thấy rõ những gì đã sẵn sàng và những gì không.

Trong thực tế, defer được sử dụng cho các script cần toàn bộ DOM hoặc thứ tự thực hiện tương đối của chúng còn async được sử dụng cho các script độc lập, như bộ đếm hoặc quảng cáo và thứ tự thực hiện của chúng không quan trọng.


Bài viết được tham khảo tại: https://javascript.info/script-async-defer
Scripts: async, defer Scripts: async, defer Reviewed by kentrung on November 04, 2019 Rating: 5

No comments:

Powered by Blogger.