Câu hỏi Tôi nên đặt các thẻ

Khi nhúng JavaScript vào tài liệu HTML, nơi thích hợp để đặt <script> và bao gồm JavaScript? Tôi dường như nhớ lại rằng bạn không phải đặt chúng trong <head> phần, nhưng đặt ở đầu của <body> cũng là phần xấu vì JavaScript sẽ phải được phân tích cú pháp trước khi trang được hiển thị hoàn toàn (hoặc một cái gì đó tương tự). Điều này dường như rời khỏi kết thúc của <body> phần như một nơi hợp lý cho <script> thẻ.

Vậy, ở đâu  đúng nơi để đặt <script> thẻ?

(Tham khảo câu hỏi này câu hỏi này, trong đó đề xuất rằng các cuộc gọi hàm JavaScript nên được di chuyển từ <a> thẻ cho <script> thẻ. Tôi đặc biệt sử dụng jQuery, nhưng các câu trả lời chung hơn cũng thích hợp.)


1162
2018-01-12 18:15


gốc


Có thể trùng lặp: stackoverflow.com/questions/143486/… - Mirko Cianfarani


Các câu trả lời:


Dưới đây là những gì xảy ra khi trình duyệt tải một trang web với <script> trên thẻ:

  1. Tìm nạp trang HTML (ví dụ: index.html)
  2. Bắt đầu phân tích cú pháp HTML
  3. Trình phân tích cú pháp gặp <script> thẻ tham chiếu tệp tập lệnh bên ngoài.
  4. Trình duyệt yêu cầu tệp tập lệnh. Trong khi đó, trình phân tích cú pháp chặn và dừng phân tích cú pháp HTML khác trên trang của bạn.
  5. Sau một thời gian, tập lệnh được tải xuống và sau đó được thực thi.
  6. Trình phân tích cú pháp tiếp tục phân tích cú pháp phần còn lại của tài liệu HTML.

Bước # 4 gây ra trải nghiệm người dùng kém. Trang web của bạn về cơ bản dừng tải cho đến khi bạn tải xuống tất cả các tập lệnh. Nếu có một điều mà người dùng ghét nó đang chờ trang web tải.

Tại sao điều này thậm chí xảy ra?

Bất kỳ tập lệnh nào cũng có thể chèn HTML của riêng nó qua document.write() hoặc các thao tác DOM khác. Điều này ngụ ý rằng trình phân tích cú pháp phải đợi cho đến khi tập lệnh được tải xuống và thực thi trước khi nó có thể phân tích cú pháp phần còn lại của tài liệu một cách an toàn. Xét cho cùng, kịch bản có thể đã chèn HTML của riêng nó vào tài liệu.

Tuy nhiên, hầu hết các nhà phát triển JavaScript không còn thao túng DOM nữa trong khi tài liệu đang tải. Thay vào đó, họ đợi cho đến khi tài liệu được tải trước khi sửa đổi nó. Ví dụ:

<!-- index.html -->
<html>
    <head>
        <title>My Page</title>
        <script type="text/javascript" src="my-script.js"></script>
    </head>
    <body>
        <div id="user-greeting">Welcome back, user</div>
    </body>
</html>

Javascript:

// my-script.js
document.addEventListener("DOMContentLoaded", function() { 
    // this function runs when the DOM is ready, i.e. when the document has been parsed
    document.getElementById("user-greeting").textContent = "Welcome back, Bart";
});

Bởi vì trình duyệt của bạn không biết my-script.js sẽ không sửa đổi tài liệu cho đến khi nó được tải xuống và thực hiện, trình phân tích cú pháp dừng phân tích cú pháp.

Đề xuất cổ

Cách tiếp cận cũ để giải quyết vấn đề này là đặt <script> thẻ ở dưới cùng của <body>, bởi vì điều này đảm bảo trình phân tích cú pháp không bị chặn cho đến cuối cùng.

Cách tiếp cận này có vấn đề riêng: trình duyệt không thể bắt đầu tải xuống các tập lệnh cho đến khi toàn bộ tài liệu được phân tích cú pháp. Đối với các trang web lớn hơn với tập lệnh và bảng định kiểu lớn, việc có thể tải xuống tập lệnh càng sớm càng tốt là rất quan trọng đối với hiệu suất. Nếu trang web của bạn không tải trong vòng 2 giây, mọi người sẽ truy cập trang web khác.

Trong một giải pháp tối ưu, trình duyệt sẽ bắt đầu tải xuống tập lệnh của bạn càng sớm càng tốt, đồng thời phân tích cú pháp phần còn lại của tài liệu của bạn.

Cách tiếp cận hiện đại

Hôm nay, các trình duyệt hỗ trợ async và defer thuộc tính trên tập lệnh. Các thuộc tính này cho trình duyệt biết rằng nó an toàn để tiếp tục phân tích cú pháp trong khi các tập lệnh đang được tải xuống.

không đồng bộ

<script type="text/javascript" src="path/to/script1.js" async></script>
<script type="text/javascript" src="path/to/script2.js" async></script>

Các tập lệnh có thuộc tính async được thực hiện không đồng bộ. Điều này có nghĩa là tập lệnh được thực thi ngay sau khi được tải xuống, mà không chặn trình duyệt trong thời gian chờ đợi.
Điều này ngụ ý rằng có thể kịch bản lệnh 2 được tải xuống và thực thi trước tập lệnh 1.

Theo http://caniuse.com/#feat=script-async, 94,57% của tất cả các trình duyệt đều hỗ trợ điều này.

hoãn lại

<script type="text/javascript" src="path/to/script1.js" defer></script>
<script type="text/javascript" src="path/to/script2.js" defer></script>

Các tập lệnh có thuộc tính trì hoãn được thực hiện theo thứ tự (tức là tập lệnh đầu tiên 1, sau đó tập lệnh 2). Điều này cũng không chặn trình duyệt.

Không giống như các tập lệnh không đồng bộ, kịch bản trì hoãn chỉ được thực hiện sau khi toàn bộ tài liệu đã được tải.

Theo http://caniuse.com/#feat=script-defer, 94,59% của tất cả các trình duyệt đều hỗ trợ điều này. 94,92% hỗ trợ ít nhất một phần.

Một lưu ý quan trọng về khả năng tương thích của trình duyệt: trong một số trường hợp, IE <= 9 có thể thực thi các tập lệnh hoãn lại theo thứ tự. Nếu bạn cần hỗ trợ các trình duyệt đó, vui lòng đọc điều này Đầu tiên!

Phần kết luận

Hiện tại, nhà nước-of-the-nghệ thuật là đặt kịch bản trong <head> và sử dụng thẻ async hoặc là defer thuộc tính. Điều này cho phép các tập lệnh của bạn được tải xuống càng sớm càng tốt mà không chặn trình duyệt của bạn.

Điều tốt là trang web của bạn vẫn nên tải chính xác trên 6% trình duyệt không hỗ trợ các thuộc tính này trong khi tăng tốc 94% khác.


1471
2018-06-05 21:20



Tôi ngạc nhiên không ai trích dẫn lời giải thích của Google ... developers.google.com/speed/docs/insights/BlockingJS - Casey Falk
Tôi không rõ ràng về những gì chạm vào DOM và những gì không. Bạn có thể làm rõ? Có an toàn để thực hiện tải async trên một cái gì đó như jquery.js không? - Doug
@Doug Ví dụ document.write hoạt động trên dom. Câu hỏi không phải nếu một kịch bản điều khiển dom, nhưng khi nào nó có. Miễn là tất cả các thao tác dom xảy ra sau domready sự kiện đã kích hoạt, bạn ổn. jQuery là một thư viện, và như vậy không - hoặc không nên - thao tác các dom của chính nó. - Bart
Câu trả lời này là sai lầm. Các trình duyệt hiện đại không ngừng phân tích cú pháp khi chúng đạt đến thẻ tập lệnh đồng bộ có thể ảnh hưởng đến HTML, chúng chỉ dừng hiển thị / thực thi và tiếp tục phân tích cú pháp một cách tối ưu để bắt đầu tải xuống các tài nguyên khác mà có thể sẽ được yêu cầu sau đó nếu không có HTML bị ảnh hưởng. - Fabio Beltramini
Tại sao async và defer thuộc tính không được sử dụng ở đâu? Ý tôi là, tôi đã xem rất nhiều nguồn HTML từ internet và tôi không thấy async và defer thuộc tính ở mọi nơi. ...? - john c. j.


Ngay trước thẻ đóng, như đã nêu trên

http://developer.yahoo.com/performance/rules.html#js_bottom

Đặt Scripts ở dưới cùng

Vấn đề do kịch bản gây ra là chúng chặn tải xuống song song. Đặc tả HTTP / 1.1 cho thấy rằng các trình duyệt tải xuống không quá hai thành phần song song trên mỗi tên máy chủ. Nếu bạn phân phát hình ảnh của mình từ nhiều tên máy chủ, bạn có thể nhận được nhiều hơn hai lượt tải xuống song song. Tuy nhiên, trong khi một tập lệnh đang tải xuống, trình duyệt sẽ không bắt đầu bất kỳ nội dung tải xuống nào khác, ngay cả trên các tên máy chủ khác nhau.


218
2018-01-12 18:18



Đồng ý với khái niệm và giải thích của nó. Nhưng điều gì xảy ra nếu người dùng bắt đầu chơi với trang. Giả sử tôi có một trình đơn thả xuống AJAX sẽ bắt đầu tải sau khi trang đã xuất hiện cho người dùng nhưng trong khi nó đang tải, người dùng nhấp vào nó! Và nếu một người dùng 'thực sự thiếu kiên nhẫn' gửi biểu mẫu thì sao? - Hemant Tank
@Hermant Bình luận cũ nhưng bạn có thể làm thủ thuật vô hiệu hóa các trường theo mặc định sau đó cho phép chúng sử dụng JS khi DOM được nạp đầy đủ. Đó là những gì Facebook dường như đang làm ngày nay. - novato
Chỉ cần thử nghiệm này với chrome, để kiểm tra xem điều này vẫn như cũ. Nó là. Bạn có thể kiểm tra sự khác biệt về thời gian tải trang của các trình duyệt của bạn tại đây. stevesouders.com/cuzillion - cypher
Nếu đây là phương pháp hay nhất, tại sao ngăn xếp ngăn xếp bao gồm tất cả các thẻ tập lệnh của chúng trong <head>? :-P - Philip
Trong một số trường hợp, đặc biệt là trong các trang web nặng ajax, tải trong đầu thực sự có thể dẫn đến thời gian tải nhanh hơn. Xem: encosia.com/dont-let-jquerys-document-ready-slow-you-down (lưu ý rằng hàm "live ()" không được dùng trong jquery, nhưng bài viết vẫn áp dụng với hàm "on ()" hoặc "delegate"). Tải trong <head> cũng có thể cần thiết để đảm bảo hành vi đúng như được chỉ ra bởi @Hermant. Cuối cùng, modernizr.com/docs khuyên bạn nên đặt các kịch bản lệnh trong <head> vì lý do được giải thích trên trang web của nó. - Nathan


Thẻ tập lệnh không chặn có thể được đặt chỉ ở bất kỳ đâu:

<script src="script.js" async></script>
<script src="script.js" defer></script>
<script src="script.js" async defer></script>
  • async tập lệnh sẽ được thực thi không đồng bộ ngay khi có sẵn
  • defer tập lệnh được thực thi khi tài liệu đã phân tích xong
  • async defer script rơi trở lại hành vi trì hoãn nếu async không được hỗ trợ

Các tập lệnh như vậy sẽ được thực thi không đồng bộ / sau khi tài liệu sẵn sàng, có nghĩa là bạn không thể thực hiện việc này:

<script src="jquery.js" async></script>
<script>jQuery(something);</script>
<!--
  * might throw "jQuery is not defined" error
  * defer will not work either
-->

Hoặc điều này:

<script src="document.write(something).js" async></script>
<!--
  * might issue "cannot write into document from an asynchronous script" warning
  * defer will not work either
-->

Hoặc điều này:

<script src="jquery.js" async></script>
<script src="jQuery(something).js" async></script>
<!--
  * might throw "jQuery is not defined" error (no guarantee which script runs first)
  * defer will work in sane browsers
-->

Hoặc điều này:

<script src="document.getElementById(header).js" async></script>
<div id="header"></div>
<!--
  * might not locate #header (script could fire before parser looks at the next line)
  * defer will work in sane browsers
-->

Có nói rằng, các tập lệnh không đồng bộ cung cấp những ưu điểm sau:

  • Tải xuống song song tài nguyên:
    Trình duyệt có thể tải xuống các tệp định kiểu, hình ảnh và các tập lệnh khác song song mà không cần chờ một tập lệnh tải xuống và thực thi.
  • Độc lập đặt hàng nguồn:
    Bạn có thể đặt các tập lệnh bên trong phần đầu hoặc phần thân mà không phải lo lắng về việc chặn (hữu ích nếu bạn đang sử dụng CMS). Tuy nhiên, lệnh thi hành vẫn là vấn đề.

Có thể phá vỡ các vấn đề về lệnh thực hiện bằng cách sử dụng các tập lệnh bên ngoài hỗ trợ gọi lại. Nhiều API JavaScript của bên thứ ba hiện hỗ trợ thực thi không chặn. Đây là một ví dụ về tải API Google Maps một cách không đồng bộ.


56
2018-02-06 11:19



Đây là câu trả lời đúng cho ngày hôm nay - bằng cách sử dụng phương pháp này có nghĩa là nó dễ dàng hơn để giữ cho các vật dụng của bạn tự chứa, không cần phải làm lạ mắt <head> bao gồm logic. - Daniel Sokolowski
Tôi bối rối vì sao bạn không thể sử dụng async hoặc là defer khi bao gồm jQuery như bạn chỉ định trong khối thứ hai của bạn: <script src="jquery.js" async></script>. Bạn có thể giải thích tại sao? Tôi nghĩ rằng tôi cần phải có thẻ async cho hiệu năng — theo câu trả lời được chấp nhận — vì vậy trang của tôi có thể tải ngay cả khi jQuery vẫn đang tải]. Cảm ơn! - elbowlobstercowstand
@ khuỷu tay 99% thời gian <script src=jquery.js> được theo sau bởi $(function(){ ... }) chặn một nơi nào đó trong trang. Tải không đồng bộ không đảm bảo rằng jQuery sẽ được tải tại trình duyệt thời gian cố phân tích các khối đó vì thế nó sẽ tăng $ không được xác định lỗi (bạn có thể không nhận được lỗi nếu jQuery được tải từ bộ nhớ cache). Tôi đã trả lời một câu hỏi về việc tải jQuery một cách không đồng bộ và bảo toàn $(function(){ ... }). Tôi sẽ xem nếu tôi có thể tìm thấy nó, hoặc bạn có thể nhìn vào câu hỏi này: stackoverflow.com/q/14811471/87015 - Salman A
@SalmanA Cảm ơn bạn! Vâng, tôi rơi vào đó 99%. Trước tiên tôi cần jquery lib để tải, sau đó phần còn lại của tôi .js tập lệnh. Khi tôi tuyên bố async hoặc là defer trên jquery thẻ tập lệnh lib, của tôi .js tập lệnh không hoạt động. tôi đã nghĩ $(function(){ ... })bảo vệ điều đó — đoán là không. Giải pháp tạm thời: Tôi không thêm defer hoặc là async trên jquery tập lệnh lib, nhưng tôi thêm async theo dõi của tôi .js tập lệnh. Lưu ý: lý do tôi đang làm bất kì điều này là làm cho Google Page Speed ​​trở nên hạnh phúc. Thx một lần nữa để được giúp đỡ! Bất kỳ lời khuyên nào khác đều được chào đón. (Hoặc một liên kết đến câu trả lời trước của bạn). :) - elbowlobstercowstand
@elbow See stackoverflow.com/a/21013975/87015, nó sẽ chỉ cung cấp cho bạn một ý tưởng nhưng không chỉ là giải pháp hoàn chỉnh. Thay vào đó, bạn có thể tìm kiếm "thư viện trình tải jquery async". - Salman A


Lời khuyên chuẩn, được thúc đẩy bởi Yahoo! Đội ngũ hiệu suất vượt trội, là đặt <script> ở cuối thân tài liệu để chúng không chặn hiển thị trang.

Nhưng có một số phương pháp tiếp cận mới hơn cung cấp hiệu suất tốt hơn, như được mô tả trong câu trả lời này về thời gian tải của tệp JavaScript Google Analytics:

Có vài trang trình bày tuyệt vời bởi Steve Souders (chuyên gia về hiệu suất phía khách hàng) về:

  • Các kỹ thuật khác nhau để tải các tệp JavaScript bên ngoài song song
  • hiệu ứng của họ khi tải thời gian và hiển thị trang
  • loại chỉ báo "đang hoạt động" mà trình duyệt hiển thị (ví dụ: 'đang tải' trong thanh trạng thái, con trỏ chuột đồng hồ cát).

36
2018-01-12 23:37





Nếu bạn đang sử dụng JQuery thì hãy đặt javascript bất cứ nơi nào bạn tìm thấy nó tốt nhất và sử dụng $(document).ready() để đảm bảo rằng mọi thứ được nạp đúng cách trước khi thực hiện bất kỳ chức năng nào.

Một lưu ý phụ: Tôi thích tất cả các thẻ tập lệnh của tôi trong <head> phần đó dường như là nơi sạch sẽ nhất.


19
2018-01-12 18:18



trong đầu ... er? <header>? - Dan Lugg
Lưu ý rằng việc sử dụng $(document).ready() không có nghĩa là bạn có thể đặt JavaScript của mình bất cứ đâu bạn thích - bạn vẫn phải đặt nó sau <script src=".../jquery.min.js"> nơi bạn bao gồm jQuery, để $ tồn tại. - Rory O'Kane
Nó không phải là tối ưu để đặt thẻ script trong phần <head> - điều này sẽ trì hoãn hiển thị phần hiển thị của trang cho đến khi các tập lệnh được tải. - CyberMonk
Không, @Dan, a header phần tử là một phần của nội dung tài liệu HTML và sẽ xảy ra một hoặc nhiều lần trong body thành phần. The thẻ head` dành cho dữ liệu siêu dữ liệu và dữ liệu không phải nội dung cho tài liệu. Đó là, những ngày này, với defer và async một nơi lý tưởng cho các thẻ script. header các phần tử chỉ nên chứa thông tin mô tả phần của tài liệu theo sau nó. - ProfK
@ProfK, Dan đã đề cập đến câu hỏi chưa được chỉnh sửa ban đầu khi anh ấy đăng bài này hơn 4 năm trước. Như bạn có thể thấy, câu hỏi đã được chỉnh sửa một năm sau đó. - kojow7


XHTML sẽ không xác thực nếu tập lệnh ở bất kỳ đâu ngoài phần tử đầu. hóa ra nó có thể ở mọi nơi.

Bạn có thể trì hoãn việc thực hiện với một cái gì đó như jQuery vì vậy nó không quan trọng nơi nó được đặt (ngoại trừ một hit hiệu suất nhỏ trong quá trình phân tích cú pháp).


8
2018-01-12 18:22



XHTML sẽ xác thực với các thẻ script trong cơ thể, cả hai đều nghiêm ngặt và chuyển tiếp. Tuy nhiên, thẻ kiểu chỉ có thể nằm trong đầu. - I.devries


<script src="myjs.js"></script>
</body>

thẻ tập lệnh phải luôn sử dụng trước cơ thể đóng hoặc là Dưới dạng HTML tập tin.

sau đó bạn có thể xem nội dung của trang trước khi tải js tập tin.

kiểm tra điều này nếu yêu cầu: http://stevesouders.com/hpws/rule-js-bottom.php


8
2017-07-16 11:16



Điều này thực sự trả lời câu hỏi. Tôi đã tự hỏi gần như tất cả các ví dụ được đăng không bao giờ đưa ra bối cảnh trực quan thích hợp của "cuối trang" - Ken Ingram
Câu trả lời này rất sai lạc và có lẽ là sai lầm nhất. Các bài viết trong Google và trong MDN gợi ý rằng JS đồng bộ (là trường hợp ở đây) luôn ngăn chặn việc xây dựng và phân tích cú pháp DOM, điều này sẽ dẫn đến việc kết xuất đầu tiên bị trì hoãn. Do đó, bạn không thể xem nội dung của trang cho đến khi tệp JS được tìm nạp và hoàn thành việc thực thi bất kể bạn đặt tệp JS của mình trong tài liệu HTML nào miễn là tệp đó đồng bộ - Lingaraju E V
Nó cũng tham chiếu các điểm được thực hiện trong năm 2009 và không còn phù hợp nữa. - toxaq


Câu trả lời thông thường (và được chấp nhận rộng rãi) là "ở dưới cùng", bởi vì sau đó toàn bộ DOM sẽ được tải trước khi bất cứ điều gì có thể bắt đầu thực hiện.

Có những người bất đồng chính kiến, vì nhiều lý do khác nhau, bắt đầu với thực tiễn sẵn có để cố ý bắt đầu thực hiện với một sự kiện tải trang.


5
2018-01-12 18:18





Tùy thuộc vào kịch bản và cách sử dụng nó tốt nhất có thể (về tải trang và thời gian hiển thị) có thể không sử dụng <script> thông thường cho mỗi lệnh, nhưng để kích hoạt động việc tải tập lệnh một cách không đồng bộ.

Có một số kỹ thuật khác nhau, nhưng thẳng nhất là sử dụng document.createElement ("script") khi sự kiện window.onload được kích hoạt. Sau đó, tập lệnh được tải đầu tiên khi chính trang đã hiển thị, do đó không ảnh hưởng đến thời gian người dùng phải đợi trang xuất hiện.

Điều này tự nhiên yêu cầu bản thân kịch bản không cần thiết cho việc hiển thị trang.

Để biết thêm thông tin, hãy xem bài đăng Tập lệnh async khớp nối bởi Steve Souders (tác giả của YSlow nhưng bây giờ tại Google).


0
2018-01-12 21:06