Thẻ Script - async & defer


547

Tôi có một vài câu hỏi về các thuộc tính async& defercho <script>thẻ mà theo tôi hiểu chỉ hoạt động trong trình duyệt HTML5.

Một trong những trang web của tôi có hai tệp JavaScript bên ngoài hiện nằm ngay phía trên </body>thẻ; đầu tiên là nguồn gốc từ google và thứ hai là một tập lệnh bên ngoài cục bộ.

Đối với tốc độ tải trang web

  1. Có bất kỳ lợi thế nào trong việc thêm asyncvào hai tập lệnh tôi có ở cuối trang không?

  2. Sẽ có bất kỳ lợi thế nào trong việc thêm asynctùy chọn vào hai tập lệnh và đặt chúng ở đầu trang trong <head>?

  3. Điều này có nghĩa là họ tải xuống khi tải trang?
  4. Tôi cho rằng điều này sẽ gây ra sự chậm trễ cho trình duyệt HTML4, nhưng liệu nó có tăng tốc độ tải trang cho trình duyệt HTML5 không?

Sử dụng <script defer src=...

  1. Việc tải hai tập lệnh bên trong <head>với thuộc tính có deferảnh hưởng như khi có tập lệnh trước </body>không?
  2. Một lần nữa tôi cho rằng điều này sẽ làm chậm trình duyệt HTML4.

Sử dụng <script async src=...

Nếu tôi có hai tập lệnh asyncđược kích hoạt

  1. Họ sẽ tải xuống cùng một lúc?
  2. Hoặc một tại một thời điểm với phần còn lại của trang?
  3. Liệu thứ tự của các kịch bản sau đó trở thành một vấn đề? Ví dụ: một tập lệnh phụ thuộc vào tập lệnh khác, vì vậy nếu tập lệnh tải xuống nhanh hơn, tập lệnh thứ hai có thể không thực thi chính xác, v.v.

Cuối cùng, tôi có nên để mọi thứ như cũ cho đến khi HTML5 được sử dụng phổ biến hơn không?


5
asynclà mới (ish), nhưng deferđã là một phần của IE kể từ IE4. deferđã được thêm vào các trình duyệt khác gần đây hơn, nhưng các phiên bản cũ hơn của các trình duyệt đó có xu hướng ít gặp hơn rất nhiều.
Alohci

3
Bây giờ, HTML5 đã trở nên rất phổ biến!
sept08

2
defercũng giống như việc đặt các tập lệnh ở dưới cùng của HTML, điều này đã phổ biến trong nhiều năm.
vsync

1
@vsync không nhất thiết phải đúng, trình duyệt sẽ tải xuống JS với thẻ defer khi phân tích cú pháp thẻ script, nhưng sẽ trì hoãn việc miễn trừ cho đến trước khi DOMContentLoaded. Tải xuống là không chặn. Việc đặt ở dưới cùng của HTML sẽ trì hoãn việc tải xuống và thực thi JS cho đến khi DOM được xây dựng, nhưng bạn vẫn sẽ phải chịu một độ trễ bổ sung bằng cách chờ tải xuống.
Brad Frost

@BradFrost - Tải xuống đang chặn theo quan điểm của tôi, theo nghĩa là nó đang chiếm băng thông internet và đối với những người có kết nối chậm, tôi thấy bắt buộc phải tải tài liệu trước và chỉ sau đó, khi nó được hiển thị, hãy bắt đầu tải xuống các tệp javascript . Điều này đúng trong trường hợp nội dung không được kết hợp chặt chẽ với javascript để hiển thị mọi thứ (như SPA )
vsync

Câu trả lời:


405

Giữ kịch bản của bạn ngay trước khi </body>. Async có thể được sử dụng với các tập lệnh nằm ở đó trong một vài trường hợp (xem thảo luận bên dưới). Defer sẽ không tạo ra nhiều sự khác biệt cho các tập lệnh nằm ở đó vì công việc phân tích cú pháp DOM đã được thực hiện khá nhiều.

Đây là một bài viết giải thích sự khác biệt giữa async và defer: http://peter.sh/experiment/asynyncous-and-deferred-javascript-execut-explained/ .

HTML của bạn sẽ hiển thị nhanh hơn trong các trình duyệt cũ hơn nếu bạn giữ các tập lệnh ở cuối phần thân ngay trước đó </body>. Vì vậy, để duy trì tốc độ tải trong các trình duyệt cũ hơn, bạn không muốn đặt chúng ở bất kỳ nơi nào khác.

Nếu tập lệnh thứ hai của bạn phụ thuộc vào tập lệnh đầu tiên (ví dụ tập lệnh thứ hai của bạn sử dụng jQuery được tải trong tập lệnh đầu tiên), thì bạn không thể đặt chúng không đồng bộ mà không cần thêm mã để kiểm soát thứ tự thực hiện, nhưng bạn có thể làm cho chúng trì hoãn vì các tập lệnh trì hoãn sẽ vẫn được thực hiện theo thứ tự, chỉ sau khi tài liệu được phân tích cú pháp. Nếu bạn có mã đó và bạn không cần các tập lệnh chạy ngay lập tức, bạn có thể làm cho chúng không đồng bộ hoặc trì hoãn.

Bạn có thể đặt các tập lệnh vào <head>thẻ và đặt chúng vào defervà việc tải tập lệnh sẽ được hoãn lại cho đến khi DOM được phân tích cú pháp và điều đó sẽ hiển thị trang nhanh trong các trình duyệt mới hỗ trợ trì hoãn, nhưng nó sẽ không giúp bạn chút nào trong các trình duyệt cũ hơn và nó không thực sự nhanh hơn việc đặt các tập lệnh ngay trước khi </body>nó hoạt động trong tất cả các trình duyệt. Vì vậy, bạn có thể thấy lý do tại sao tốt nhất là đặt chúng ngay trước đó </body>.

Async hữu ích hơn khi bạn thực sự không quan tâm khi tải tập lệnh và không có gì khác phụ thuộc vào người dùng phụ thuộc vào việc tải tập lệnh đó. Ví dụ được trích dẫn thường xuyên nhất để sử dụng async là tập lệnh phân tích như Google Analytics mà bạn không muốn chờ đợi điều gì và nó không khẩn cấp để chạy sớm và nó đứng một mình nên không có gì khác phụ thuộc vào nó.

Thông thường thư viện jQuery không phải là ứng cử viên tốt cho async vì các tập lệnh khác phụ thuộc vào nó và bạn muốn cài đặt trình xử lý sự kiện để trang của bạn có thể bắt đầu phản hồi các sự kiện của người dùng và bạn có thể cần chạy một số mã khởi tạo dựa trên jQuery để thiết lập trạng thái ban đầu của trang. Nó có thể được sử dụng không đồng bộ, nhưng các tập lệnh khác sẽ phải được mã hóa để không thực thi cho đến khi jQuery được tải.


8
Defer nên chạy chúng theo thứ tự tĩnh, nhưng chạy trước khi tải nội dung dom. Điều đó không có nghĩa là đặt nó vào đầu sẽ nhanh hơn, vì nó có thể bắt đầu tải chúng TRƯỚC KHI html cơ thể được phân tích cú pháp?
Kevin

9
Bạn nói rằng việc đặt các tập lệnh vào headvà đặt chúng defersẽ không nhanh hơn việc đặt chúng trước đó </body>, nhưng từ những gì tôi đã đọc thì không chính xác. Hãy suy nghĩ về nó - nếu bạn đặt các tập lệnh vào <head>, thì chúng sẽ bắt đầu tải xuống ngay lập tức, trong khi nếu chúng đúng trước </body>thì tất cả các yếu tố khác sẽ tải xuống trước.
Nate

12
@Nate - Nó sẽ không làm cho tài liệu của bạn tải nhanh hơn nữa, đó là quan điểm của tôi. Bạn đúng rằng nó có thể cải thiện việc tải tập lệnh sớm hơn, nhưng nó cũng có thể làm chậm quá trình tải tài liệu và nội dung của nó vì bạn đang sử dụng một số băng thông của mình và sử dụng một trong các kết nối giới hạn mà trình duyệt sẽ thực hiện với một máy chủ nhất định để tải tập lệnh trong khi nó cũng đang cố tải nội dung của bạn.
jfriend00

4
"Nếu tập lệnh thứ hai của bạn phụ thuộc vào tập lệnh đầu tiên ... thì bạn không thể làm cho chúng không đồng bộ hoặc trì hoãn" - điều đó không đúng, với việc trì hoãn chúng thực hiện theo thứ tự.
DisgruntledGoat

2
Tại thời điểm này, yêu cầu </ body> không thực sự cần thiết với sự phát triển của trình duyệt kể từ năm 2012 khi câu trả lời này được đăng.
bgcode

843

Hình ảnh này giải thích thẻ script bình thường, async và defer

nhập mô tả hình ảnh ở đây

  • Các tập lệnh Async được thực thi ngay khi tập lệnh được tải, do đó, nó không đảm bảo thứ tự thực hiện (tập lệnh bạn đưa vào cuối có thể thực thi trước tập tin tập lệnh đầu tiên)

  • Các kịch bản trì hoãn đảm bảo thứ tự thực hiện trong đó chúng xuất hiện trong trang.

Tham chiếu liên kết này: http://www.growingwiththeweb.com/2014/02/async-vs-defer-attribut.html


Tôi nghĩ rằng một ví dụ với nhiều tập lệnh sẽ tốt hơn để minh họa trình tự của chúng
vsync

4
@writofmandamus Hình như asyncsẽ thắng. Xem stackoverflow.com/questions/13821151/ cấp
Đức ông

Cảm ơn lời giải thích tốt. Tuy nhiên, hình ảnh không được chia tỷ lệ. Trong trường hợp chỉ có <script>thẻ, tổng thời lượng tải trang dài hơn theo thời gian để tải xuống tệp tập lệnh.
arni

@BhastaHirani Theo trang web này , sử dụng cả async và defer trong cùng một thẻ script sẽ sử dụng async nếu trình duyệt hỗ trợ hoặc quay lại trì hoãn nếu nó không hỗ trợ async, nhưng hỗ trợ defer. Các hành vi khá khác nhau, vì vậy tôi sẽ không khuyên bạn sử dụng cả hai, vì kết quả là không thể đoán trước và có thể là một nguồn tuyệt vời cho các lỗi.
Adrian Wiik

@arni Chỉ khi băng thông được sử dụng đầy đủ, nó hiếm khi được. Và cả hai bản tải xuống sẽ chia sẻ băng thông, không chặn một. Hơn nữa: những hình ảnh này hiển thị phân tích màu xanh lục, không tải xuống.
Robert Siemer

213

HTML5: async,defer

Trong HTML5, bạn có thể báo cho trình duyệt biết khi nào nên chạy mã JavaScript của mình. Có 3 khả năng:

<script       src="myscript.js"></script>

<script async src="myscript.js"></script>

<script defer src="myscript.js"></script>
  1. Không có asynchoặc defer, trình duyệt sẽ chạy tập lệnh của bạn ngay lập tức, trước khi hiển thị các phần tử bên dưới thẻ tập lệnh của bạn.

  2. Với async(không đồng bộ), trình duyệt sẽ tiếp tục tải trang HTML và hiển thị nó trong khi trình duyệt tải và thực thi tập lệnh cùng một lúc.

  3. Với defer, trình duyệt sẽ chạy tập lệnh của bạn khi trang phân tích xong. (không cần hoàn tất tải xuống tất cả các tệp hình ảnh. Điều này là tốt.)


yêu cầu mẫu blogger.com async=""trước khi xác thực và lưu các thay đổi mẫu.
noobninja

1
Lưu ý: không có gì đảm bảo rằng các tập lệnh sẽ chạy theo thứ tự chúng được chỉ định bằng Async. "Vì vậy, nếu tập lệnh thứ hai của bạn phụ thuộc vào tập lệnh đầu tiên, hãy tránh Async."
Faisal Naseer

2
async- Các tập lệnh được thực thi ngay khi chúng được tải xuống, không xem xét thứ tự của chúng trong tệp HTML.
vsync

30

Cả hai asyncdefertập lệnh bắt đầu tải xuống ngay lập tức mà không tạm dừng trình phân tích cú pháp và cả hai đều hỗ trợ onloadtrình xử lý tùy chọn để giải quyết nhu cầu chung để thực hiện khởi tạo phụ thuộc vào tập lệnh.

Sự khác biệt giữa asyncdefertrung tâm xung quanh khi tập lệnh được thực thi. Mỗi asynctập lệnh thực thi tại cơ hội đầu tiên sau khi tải xuống xong và trước sự kiện tải của cửa sổ. Điều này có nghĩa là có thể (và có khả năng) asynccác tập lệnh không được thực thi theo thứ tự xuất hiện trong trang. Trong khi đó, các defertập lệnh, mặt khác, được đảm bảo được thực thi theo thứ tự chúng xảy ra trong trang. Việc thực thi đó bắt đầu sau khi phân tích cú pháp hoàn tất, nhưng trước DOMContentLoadedsự kiện của tài liệu .

Nguồn & thông tin chi tiết: tại đây .


25

Đối mặt với cùng một loại vấn đề và giờ đã hiểu rõ ràng cả hai sẽ hoạt động như thế nào. Hy vọng liên kết tham khảo này sẽ hữu ích ...

Không đồng bộ

Khi bạn thêm thuộc tính async vào thẻ script của mình, điều sau đây sẽ xảy ra.

<script src="myfile1.js" async></script>
<script src="myfile2.js" async></script>
  1. Thực hiện các yêu cầu song song để lấy các tập tin.
  2. Tiếp tục phân tích tài liệu như thể nó không bao giờ bị gián đoạn.
  3. Thực thi các tập lệnh riêng lẻ tại thời điểm các tập tin được tải xuống.

Hoãn lại

Defer rất giống với async với một điểm khác biệt chính. Đây là những gì xảy ra khi một trình duyệt bắt gặp một tập lệnh với thuộc tính defer.

<script src="myfile1.js" defer></script>
<script src="myfile2.js" defer></script>
  1. Thực hiện các yêu cầu song song để tìm nạp các tệp riêng lẻ.
  2. Tiếp tục phân tích tài liệu như thể nó không bao giờ bị gián đoạn.
  3. Kết thúc phân tích tài liệu ngay cả khi các tệp script đã được tải xuống.
  4. Thực hiện từng tập lệnh theo thứ tự chúng đã gặp trong tài liệu.

Tham khảo: Sự khác biệt giữa Async và Defer


7

asyncdefersẽ tải xuống tệp trong quá trình phân tích cú pháp HTML. Cả hai sẽ không làm gián đoạn trình phân tích cú pháp.

  • Kịch bản với asyncthuộc tính sẽ được thực thi khi nó được tải xuống. Trong khi tập lệnh với deferthuộc tính sẽ được thực thi sau khi hoàn thành phân tích cú pháp DOM.

  • Các tập lệnh được tải với asynckhông đảm bảo bất kỳ thứ tự. Trong khi các tập lệnh được tải với deferthuộc tính duy trì thứ tự xuất hiện trên DOM.

Sử dụng <script async>khi tập lệnh không dựa vào bất cứ điều gì. khi kịch bản phụ thuộc vào sử dụng.

Giải pháp tốt nhất sẽ là thêm phần dưới cùng của cơ thể. Sẽ không có vấn đề gì với việc chặn hoặc kết xuất.


Chỉ muốn làm rõ một số điều ở đây, có hai điều đang xảy ra ở đây 1. Tải xuống tài nguyên 2. Thực thi tài nguyên. Tải xuống tài nguyên trong cả hai trường hợp (async và defer) đều không chặn, có nghĩa là, chúng không chặn phân tích cú pháp html, trong khi thực thi trong async chặn phân tích cú pháp và trong trường hợp trì hoãn, việc thực thi diễn ra sau khi đánh dấu html được phân tích cú pháp, do đó không chặn trong trường hợp này.
pOoOf

5

Tôi nghĩ Jake Archibald đã trình bày cho chúng tôi một số thông tin chuyên sâu về năm 2013 có thể tăng thêm tính tích cực cho chủ đề này:

https://www.html5rocks.com/en/tutorials/speed/script-loading/

Chén thánh đang có một tập hợp các tập lệnh tải xuống ngay lập tức mà không chặn kết xuất và thực thi càng sớm càng tốt theo thứ tự chúng được thêm vào. Thật không may, HTML ghét bạn và sẽ không cho phép bạn làm điều đó.

(...)

Câu trả lời thực sự nằm ở thông số HTML5, mặc dù nó bị ẩn ở cuối phần tải tập lệnh. " Thuộc tính IDL async kiểm soát xem phần tử có thực thi không đồng bộ hay không. Nếu cờ" bắt buộc không đồng bộ "của phần tử được đặt, thì khi nhận, thuộc tính IDL async phải trả về đúng và khi cài đặt," bắt buộc không đồng bộ " trước tiên phải bỏ cờ ".

(...)

Các tập lệnh được tạo động và thêm vào tài liệu theo mặc định là không đồng bộ , chúng không chặn kết xuất và thực thi ngay khi chúng tải xuống, có nghĩa là chúng có thể xuất hiện theo thứ tự sai. Tuy nhiên, chúng tôi có thể đánh dấu rõ ràng chúng là không đồng bộ:

[
    '//other-domain.com/1.js',
    '2.js'
].forEach(function(src) {
    var script = document.createElement('script');
    script.src = src;
    script.async = false;
    document.head.appendChild(script);
});

Điều này mang lại cho các tập lệnh của chúng tôi một hỗn hợp các hành vi không thể đạt được với HTML đơn giản. Bằng cách không rõ ràng không đồng bộ, các tập lệnh được thêm vào hàng đợi thực thi, cùng hàng đợi chúng được thêm vào trong ví dụ HTML đơn giản đầu tiên của chúng tôi. Tuy nhiên, bằng cách được tạo động, chúng được thực hiện bên ngoài phân tích tài liệu, do đó kết xuất không bị chặn trong khi chúng được tải xuống (đừng nhầm lẫn việc tải tập lệnh không đồng bộ với XHR đồng bộ hóa, điều này không bao giờ là một điều tốt).

Tập lệnh ở trên phải được đưa vào nội tuyến trong phần đầu của trang, xếp hàng tải xuống tập lệnh càng sớm càng tốt mà không làm gián đoạn kết xuất liên tục và thực thi càng sớm càng tốt theo thứ tự bạn đã chỉ định. Bạn có thể tải xuống miễn phí trước khi tải xuống 1.js, nhưng nó sẽ không được thực thi cho đến khi mà 1. 1. 1.ss đã tải xuống và thực hiện thành công hoặc không thực hiện được. Tiếng hoan hô! async-download nhưng thực hiện theo lệnh !

Tuy nhiên, đây có thể không phải là cách nhanh nhất để tải tập lệnh:

(...) Với ví dụ trên, trình duyệt phải phân tích cú pháp và thực thi tập lệnh để khám phá tập lệnh nào sẽ tải xuống. Điều này ẩn tập lệnh của bạn từ máy quét tải trước. Các trình duyệt sử dụng các máy quét này để khám phá các tài nguyên trên các trang bạn có thể truy cập tiếp theo hoặc khám phá các tài nguyên trang trong khi trình phân tích cú pháp bị chặn bởi một tài nguyên khác.

Chúng tôi có thể thêm khả năng khám phá trở lại bằng cách đặt phần này vào phần đầu của tài liệu:

<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">

Điều này cho trình duyệt biết trang cần 1.js và 2.js. link [rel = subresource] tương tự như link [rel = prefetch], nhưng với ngữ nghĩa khác nhau. Thật không may, hiện tại nó chỉ được hỗ trợ trong Chrome và bạn phải khai báo tập lệnh nào sẽ tải hai lần, một lần qua các thành phần liên kết và một lần nữa trong tập lệnh của bạn.

Sửa chữa: Ban đầu tôi đã nói rằng những thứ này được chọn bởi trình quét trước, chúng không phải, chúng được chọn bởi trình phân tích cú pháp thông thường. Tuy nhiên, trình quét tải trước có thể chọn những thứ này, nhưng nó vẫn chưa có, trong khi các tập lệnh được bao gồm bởi mã thực thi không bao giờ có thể được tải trước. Cảm ơn Yoav Weiss đã sửa chữa tôi trong các bình luận.


1

Có vẻ như hành vi của defer và async phụ thuộc vào trình duyệt, ít nhất là trong giai đoạn thực thi. LƯU Ý, trì hoãn chỉ áp dụng cho các tập lệnh bên ngoài. Tôi giả sử async theo cùng một mô hình.

Trong IE 11 trở xuống, thứ tự có vẻ như thế này:

  • async (có thể thực thi một phần trong khi tải trang)
  • không có (có thể thực thi trong khi tải trang)
  • defer (thực thi sau khi tải trang, tất cả các defer theo thứ tự vị trí trong tệp)

Trong Edge, Webkit, v.v., thuộc tính async dường như bị bỏ qua hoặc được đặt ở cuối:

  • data-Pagespeed-no-defer (thực thi trước bất kỳ tập lệnh nào khác, trong khi trang đang tải)
  • không có (có thể thực thi trong khi trang đang tải)
  • defer (đợi cho đến khi DOM được tải, tất cả các defer theo thứ tự vị trí trong tệp)
  • async (dường như đợi cho đến khi DOM được tải)

Trong các trình duyệt mới hơn, thuộc tính data-pagepeed-no-defer chạy trước bất kỳ tập lệnh bên ngoài nào khác. Cái này dành cho các script không phụ thuộc vào DOM.

LƯU Ý: Sử dụng defer khi bạn cần một lệnh thực thi rõ ràng các tập lệnh bên ngoài. Điều này báo cho trình duyệt thực thi tất cả các tập lệnh bị trì hoãn theo thứ tự vị trí trong tệp.

ASIDE: Kích thước của các javascripts bên ngoài có vấn đề khi tải ... nhưng không ảnh hưởng đến thứ tự thực hiện.

Nếu bạn lo lắng về hiệu suất của các tập lệnh của mình, bạn có thể muốn xem xét thu nhỏ hoặc đơn giản là tải chúng một cách linh hoạt với một XMLHttpRequest.


data-pagespeed-no-deferlà một thuộc tính được sử dụng bởi mô-đun PageSpeed phía máy chủ . Các thuộc tính của nó tự không có tác dụng trong bất kỳ trình duyệt. data-pagespeed-no-defer
Qtax
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.