Trình duyệt tải JavaScript như thế nào

Không, đợi đã, quay lại. Tôi biết điều này nghe có vẻ trần tục và đơn giản, nhưng hãy nhớ rằng, điều này đang xảy ra trong trình duyệt nơi đơn giản về mặt lý thuyết trở thành một lỗ hổng dựa trên di sản. Biết những điều kỳ quặc này cho phép bạn chọn cách tải tập lệnh nhanh nhất, ít gây gián đoạn nhất. Nếu bạn đang có một lịch trình chặt chẽ, hãy bỏ qua phần tham khảo nhanh

Đối với người mới bắt đầu, đây là cách thông số kỹ thuật xác định các cách khác nhau mà tập lệnh có thể tải xuống và thực thi

Trình duyệt tải JavaScript như thế nào
WHATWG khi tải tập lệnh

Giống như tất cả các thông số kỹ thuật của WHATWG, ban đầu nó trông giống như hậu quả của một quả bom chùm trong một nhà máy sản xuất tranh ghép mảnh, nhưng một khi bạn đã đọc nó lần thứ 5 và lau máu trên mắt, nó thực sự khá thú vị

kịch bản đầu tiên của tôi bao gồm #

<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>

Ahh, đơn giản hạnh phúc. Tại đây, trình duyệt sẽ tải xuống song song cả hai tập lệnh và thực thi chúng ngay khi có thể, duy trì thứ tự của chúng. “2. js” sẽ không thực thi cho đến khi “1. js” đã thực thi (hoặc không thực hiện được), “1. js” sẽ không thực thi cho đến khi tập lệnh hoặc biểu định kiểu trước đó được thực thi, v.v.

Thật không may, trình duyệt chặn hiển thị thêm trang trong khi tất cả điều này đang diễn ra. Điều này là do các API DOM từ “thời đại đầu tiên của web” cho phép các chuỗi được thêm vào nội dung mà trình phân tích cú pháp đang xem qua, chẳng hạn như

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
9. Các trình duyệt mới hơn sẽ tiếp tục quét hoặc phân tích cú pháp tài liệu trong nền và kích hoạt tải xuống nội dung bên ngoài mà nó có thể cần (js, hình ảnh, css, v.v.), nhưng hiển thị vẫn bị chặn

Đây là lý do tại sao những điều tuyệt vời và tốt đẹp của thế giới hiệu suất khuyên bạn nên đặt các thành phần tập lệnh ở cuối tài liệu của mình, vì nó chặn càng ít nội dung càng tốt. Thật không may, điều đó có nghĩa là trình duyệt không nhìn thấy tập lệnh của bạn cho đến khi nó tải xuống tất cả HTML của bạn và tại thời điểm đó, nó bắt đầu tải xuống nội dung khác, chẳng hạn như CSS, hình ảnh và iframe. Các trình duyệt hiện đại đủ thông minh để ưu tiên JavaScript hơn hình ảnh, nhưng chúng tôi có thể làm tốt hơn

Cảm ơn IE. (không, tôi không mỉa mai đâu) #

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>

Microsoft đã nhận ra những vấn đề về hiệu suất này và giới thiệu tính năng “defer” trong Internet Explorer 4. Về cơ bản, điều này nói rằng “Tôi hứa sẽ không đưa nội dung vào trình phân tích cú pháp bằng cách sử dụng những thứ như

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
9. Nếu tôi vi phạm lời hứa đó, bạn có thể tự do trừng phạt tôi theo bất kỳ cách nào bạn thấy phù hợp ”. Thuộc tính này đã biến nó thành HTML4 và xuất hiện trong các trình duyệt khác

Trong ví dụ trên, trình duyệt sẽ tải xuống song song cả hai tập lệnh và thực thi chúng ngay trước khi

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
1 kích hoạt, duy trì thứ tự của chúng

Giống như một quả bom chùm trong nhà máy cừu, “trì hoãn” trở thành một mớ hỗn độn. Giữa các thuộc tính “src” và “defer” và thẻ tập lệnh so với tập lệnh được thêm động, chúng tôi có 6 mẫu thêm tập lệnh. Tất nhiên, các trình duyệt không thống nhất về thứ tự thực thi. Mozilla đã viết một bài tuyệt vời về vấn đề này khi nó tồn tại trở lại vào năm 2009

WHATWG đã làm cho hành vi trở nên rõ ràng, tuyên bố “defer” không ảnh hưởng đến các tập lệnh được thêm động hoặc thiếu “src”. Mặt khác, các tập lệnh bị trì hoãn sẽ chạy sau khi tài liệu đã được phân tích cú pháp, theo thứ tự chúng được thêm vào

Cảm ơn IE. (ok, bây giờ tôi đang mỉa mai) #

Nó cho, nó lấy đi. Thật không may, có một lỗi khó chịu trong IE4-9 có thể khiến các tập lệnh thực thi theo thứ tự không mong muốn. Đây là những gì xảy ra

1. js #

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');

2. js #

console.log('3');

Giả sử có một đoạn trên trang, thứ tự dự kiến ​​của nhật ký là [1, 2, 3], mặc dù trong IE9 trở xuống, bạn nhận được [1, 3, 2]. Các hoạt động DOM cụ thể khiến IE tạm dừng thực thi tập lệnh hiện tại và thực thi các tập lệnh đang chờ xử lý khác trước khi tiếp tục

Tuy nhiên, ngay cả trong các triển khai không có lỗi, chẳng hạn như IE10 và các trình duyệt khác, việc thực thi tập lệnh bị trì hoãn cho đến khi toàn bộ tài liệu được tải xuống và phân tích cú pháp. Điều này có thể thuận tiện nếu bạn vẫn sẽ đợi

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
1, nhưng nếu bạn muốn thực sự tích cực với hiệu suất, bạn có thể bắt đầu thêm người nghe và khởi động sớm hơn…

HTML5 để giải cứu #

________số 8

HTML5 đã cung cấp cho chúng tôi một thuộc tính mới, “async”, giả định rằng bạn sẽ không sử dụng

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
9, nhưng không đợi cho đến khi tài liệu được phân tích cú pháp để thực thi. Trình duyệt sẽ tải xuống song song cả hai tập lệnh và thực thi chúng ngay khi có thể

Thật không may, vì họ sẽ thực hiện càng sớm càng tốt, “2. js” có thể thực thi trước “1. js”. Điều này tốt nếu họ độc lập, có lẽ “1. js” là tập lệnh theo dõi không liên quan gì đến “2. js”. Nhưng nếu “1. js” là một bản sao CDN của jQuery mà “2. js” tùy thuộc vào, trang của bạn sẽ bị phủ đầy lỗi, giống như một quả bom chùm trong… Tôi không biết… Tôi không có gì cho trang này

Tôi biết chúng ta cần gì, một thư viện JavaScript. #

Chén thánh đang có một bộ 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 đó

Sự cố đã được JavaScript giải quyết theo một số cách. Một số yêu cầu bạn thực hiện các thay đổi đối với JavaScript của mình, gói nó trong một lệnh gọi lại mà thư viện gọi theo đúng thứ tự (ví dụ: RequireJS). Những người khác sẽ sử dụng XHR để tải xuống song song sau đó

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
4 theo đúng thứ tự, điều này không hoạt động đối với các tập lệnh trên miền khác trừ khi chúng có tiêu đề CORS và trình duyệt hỗ trợ nó. Một số thậm chí đã sử dụng các bản hack siêu ma thuật, như LabJS

Các vụ hack liên quan đến việc lừa trình duyệt tải xuống tài nguyên theo cách sẽ kích hoạt một sự kiện khi hoàn thành, nhưng tránh thực hiện nó. Trong LabJS, tập lệnh sẽ được thêm vào với loại mime không chính xác, ví dụ:

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
5. Khi tất cả các tập lệnh đã được tải xuống, chúng sẽ được thêm lại với loại chính xác, hy vọng trình duyệt sẽ lấy chúng trực tiếp từ bộ đệm và thực thi chúng ngay lập tức, theo thứ tự. Điều này phụ thuộc vào hành vi thuận tiện nhưng không xác định và đã bị hỏng khi HTML5 tuyên bố các trình duyệt không nên tải xuống các tập lệnh có loại không được nhận dạng. Đáng chú ý là LabJS đã thích nghi với những thay đổi này và hiện sử dụng kết hợp các phương pháp trong bài viết này

Tuy nhiên, trình tải tập lệnh có vấn đề về hiệu suất của riêng chúng, bạn phải đợi JavaScript của thư viện tải xuống và phân tích cú pháp trước khi bất kỳ tập lệnh nào mà nó quản lý có thể bắt đầu tải xuống. Ngoài ra, chúng ta sẽ tải trình tải tập lệnh như thế nào?

Về cơ bản, nếu bạn phải tải xuống một tệp tập lệnh bổ sung trước khi nghĩ đến việc tải xuống các tập lệnh khác, thì bạn đã thua trong trận chiến hiệu suất ngay tại đó

DOM để giải cứu #

Câu trả lời thực sự nằm trong thông số kỹ thuật 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ờ "force-async" của phần tử được đặt, thì khi nhận, thuộc tính IDL không đồng bộ phải trả về true và khi cài đặt, cờ "force-async" trước tiên phải được bỏ đặt…

Hãy dịch nó thành “Earthling”

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
2

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

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
3

Đ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 bằng HTML thuần túy. Bằng cách rõ ràng là không đồng bộ, các tập lệnh được thêm vào hàng đợi thực thi, chính hàng đợi mà 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 thi bên ngoài phân tích cú pháp tài liệu, do đó, quá trình hiển thị không bị chặn trong khi chúng được tải xuống (đừng nhầm lẫn giữa việc tải tập lệnh không đồng bộ với XHR đồng bộ, điều này không bao giờ là đ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 quá trình hiển thị lũy tiến và thực thi càng sớm càng tốt theo thứ tự bạn đã chỉ định. “2. js” được tải xuống miễn phí trước “1. js”, nhưng nó sẽ không được thực thi cho đến khi “1. js” đã được tải xuống và thực thi thành công hoặc không thực hiện được. Tiếng hoan hô. tải xuống không đồng bộ nhưng thực thi theo thứ tự

Tải tập lệnh theo cách này được hỗ trợ bởi mọi thứ hỗ trợ thuộc tính async, ngoại trừ Safari 5. 0 (5. 1 cũng được). Ngoài ra, tất cả các phiên bản của Firefox và Opera đều được hỗ trợ dưới dạng các phiên bản không hỗ trợ thuộc tính async thực thi các tập lệnh được thêm động một cách thuận tiện theo thứ tự chúng được thêm vào tài liệu.

Đó là cách nhanh nhất để tải tập lệnh phải không?

Chà, nếu bạn đang tự động quyết định sẽ tải tập lệnh nào, vâng, nếu không, có lẽ không. 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 cần tải xuống. Điều này ẩn tập lệnh của bạn khỏi máy quét tải trước. Trình duyệt sử dụng các trình quét này để khám phá tài nguyên trên các trang mà bạn có thể sẽ truy cập tiếp theo hoặc khám phá 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

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
4

Điều này cho trình duyệt biết trang cần 1. js và 2. js.

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
6 tương tự như
console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
7, nhưng khác về ngữ nghĩa. Rất tiếc, tính năng này hiệ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 thông qua các phần tử liên kết và một lần nữa trong tập lệnh của bạn

Điều chỉnh. Ban đầu tôi đã nói rằng những thứ này được chọn bởi trình quét tải 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 lỗi cho tôi trong phần nhận xét

Tôi thấy bài viết này chán nản #

Tình hình đang buồn và bạn nên cảm thấy chán nản. Không có cách khai báo nào để tải xuống các tập lệnh một cách nhanh chóng và không đồng bộ trong khi kiểm soát thứ tự thực thi. Với HTTP2/SPDY, bạn có thể giảm chi phí yêu cầu đến mức phân phối tập lệnh trong nhiều tệp nhỏ có thể lưu vào bộ nhớ cache riêng lẻ có thể là cách nhanh nhất. Tưởng tượng

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
7

Mỗi tập lệnh nâng cao xử lý một thành phần trang cụ thể, nhưng yêu cầu các chức năng tiện ích trong phần phụ thuộc. js. Lý tưởng nhất là chúng tôi muốn tải xuống tất cả một cách không đồng bộ, sau đó thực thi các tập lệnh nâng cao càng sớm càng tốt, theo bất kỳ thứ tự nào, nhưng sau các phần phụ thuộc. js. Đó là tăng cường tiến bộ lũy tiến. Thật không may, không có cách khai báo nào để đạt được điều này trừ khi chính các tập lệnh được sửa đổi để theo dõi trạng thái tải của các phụ thuộc. js. Ngay cả async=false cũng không giải quyết được vấn đề này, khi thực hiện nâng cao-10. js sẽ chặn ngày 9-1. Trên thực tế, chỉ có một trình duyệt có thể thực hiện điều này mà không cần hack…

IE có một ý tưởng. #

IE tải tập lệnh khác với các trình duyệt khác

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
8

IE bắt đầu tải xuống “bất cứ thứ gì. js”, các trình duyệt khác sẽ không bắt đầu tải xuống cho đến khi tập lệnh được thêm vào tài liệu. IE cũng có một sự kiện, “readystatechange” và thuộc tính, “readystate”, cho chúng ta biết tiến trình tải. Điều này thực sự rất hữu ích vì nó cho phép chúng tôi kiểm soát việc tải và thực thi các tập lệnh một cách độc lập

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
0

Chúng ta có thể xây dựng các mô hình phụ thuộc phức tạp bằng cách chọn thời điểm thêm tập lệnh vào tài liệu. IE đã hỗ trợ mô hình này kể từ phiên bản 6. Khá thú vị, nhưng nó vẫn gặp vấn đề về khả năng phát hiện của trình tải trước giống như

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
8

Đầy đủ. Tôi nên tải tập lệnh như thế nào?

Ừ ừ. Nếu bạn muốn tải tập lệnh theo cách không chặn kết xuất, không lặp lại và có hỗ trợ trình duyệt tuyệt vời, thì đây là những gì tôi đề xuất

<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>

Điều đó. Ở cuối phần tử body. Vâng, trở thành một nhà phát triển web cũng giống như trở thành Vua Sisyphus (bùng nổ. 100 điểm hipster để tham khảo thần thoại Hy Lạp. ). Hạn chế trong HTML và trình duyệt ngăn chúng tôi làm tốt hơn nhiều

Tôi hy vọng các mô-đun JavaScript sẽ cứu chúng tôi bằng cách cung cấp một cách khai báo không chặn để tải các tập lệnh và kiểm soát thứ tự thực thi, mặc dù điều này yêu cầu các tập lệnh phải được viết dưới dạng mô-đun

Eww, phải có một cái gì đó tốt hơn chúng ta có thể sử dụng bây giờ?

Công bằng mà nói, đối với điểm thưởng, nếu bạn muốn thực sự tích cực về hiệu suất và không ngại một chút phức tạp và lặp lại, bạn có thể kết hợp một số thủ thuật ở trên

Đầu tiên, chúng tôi thêm khai báo nguồn phụ, cho trình tải trước

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
4

Sau đó, nội tuyến trong phần đầu của tài liệu, chúng tôi tải các tập lệnh của mình bằng JavaScript, sử dụng

console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
8, quay lại quá trình tải tập lệnh dựa trên trạng thái sẵn sàng của IE, quay lại trì hoãn

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
3

Một vài thủ thuật và thu nhỏ sau, đó là 362 byte + URL tập lệnh của bạn

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
4

Có đáng để thêm byte so với một tập lệnh đơn giản không? . Mặt khác, có lẽ là không, hãy gắn bó với phương pháp kết thúc cơ thể đơn giản

Phew, giờ thì tôi biết tại sao phần tải tập lệnh WHATWG lại rộng lớn như vậy. tôi cần một thức uống

Tham khảo nhanh #

Các phần tử tập lệnh đơn giản #

<script src="//other-domain.com/1.js" defer>script>
<script src="2.js" defer>script>
5

Thông số kỹ thuật nói. Tải cùng nhau, thực hiện theo thứ tự ngay sau khi tất cả tải xuống. firefox <3. 6, Opera nói. Tôi không biết thứ “không đồng bộ” này là gì, nhưng tình cờ là tôi thực thi các tập lệnh được thêm qua JS theo thứ tự chúng được thêm vào. Safari 5. 0 nói. Tôi hiểu "không đồng bộ", nhưng không hiểu cách đặt thành "false" với JS. Tôi sẽ thực thi tập lệnh của bạn ngay khi chúng hạ cánh, theo bất kỳ thứ tự nào. IE <10 nói. Không biết gì về "không đồng bộ", nhưng có một cách giải quyết khác là sử dụng "onreadystatechange". Các trình duyệt khác màu đỏ nói. Tôi không hiểu điều "không đồng bộ" này, tôi sẽ thực thi tập lệnh của bạn ngay khi chúng hạ cánh, theo bất kỳ thứ tự nào. Mọi thứ khác nói. Tôi là bạn của bạn, chúng ta sẽ làm điều này theo sách

Trình duyệt xử lý JavaScript và HTML như thế nào?

From previous explanations, the browser reads raw bytes of the HTML file from the disk (or network) and transforms that into characters. The characters are further parsed into tokens. As soon as the parser reaches the line with , a request is made to fetch the CSS file, style.

Làm thế nào để một trang web tải trong trình duyệt?

Trình duyệt tra cứu địa chỉ IP của miền . Để làm điều đó, nó cần tra cứu địa chỉ IP của máy chủ lưu trữ trang web bằng tên miền bạn đã nhập. After you've typed the URL into your browser and pressed enter, the browser needs to figure out which server on the Internet to connect to. To do that, it needs to look up the IP address of the server hosting the website using the domain you typed in.

JavaScript hoạt động như thế nào trong nội bộ?

JavaScript là ngôn ngữ lập trình đơn luồng, có nghĩa là nó có một ngăn xếp cuộc gọi duy nhất . Do đó, nó có thể làm một việc tại một thời điểm. Ngăn xếp cuộc gọi là một cấu trúc dữ liệu ghi lại về cơ bản chúng ta đang ở đâu trong chương trình. Nếu chúng ta bước vào một chức năng, chúng ta sẽ đặt nó lên trên cùng của ngăn xếp.

Trình duyệt tải HTML như thế nào?

Khi một trang web được tải, trước tiên trình duyệt sẽ đọc văn bản HTML và xây dựng Cây DOM từ đó. Sau đó, nó xử lý CSS cho dù đó là CSS nội tuyến, được nhúng hay CSS bên ngoài và xây dựng Cây CSSOM từ nó. Sau khi những cây này được xây dựng, thì nó sẽ xây dựng Render-Tree từ nó