Sekwencja ładowania i wykonywania strony internetowej?

244

Zrobiłem kilka projektów internetowych, ale nie myślę za dużo o sekwencji ładowania i wykonania zwykłej strony internetowej. Ale teraz muszę znać szczegóły. Trudno jest znaleźć odpowiedzi od Google lub SO, więc stworzyłem to pytanie.

Przykładowa strona wygląda następująco:

<html>
 <head>
  <script src="jquery.js" type="text/javascript"></script>
  <script src="abc.js" type="text/javascript">
  </script>
  <link rel="stylesheets" type="text/css" href="abc.css"></link>
  <style>h2{font-wight:bold;}</style>
  <script>
  $(document).ready(function(){
     $("#img").attr("src", "kkk.png");
  });
 </script>
 </head>
 <body>
    <img id="img" src="abc.jpg" style="width:400px;height:300px;"/>
    <script src="kkk.js" type="text/javascript"></script>
 </body>
</html>

Oto moje pytania:

  1. Jak ładuje się ta strona?
  2. Jaka jest sekwencja ładowania?
  3. Kiedy wykonywany jest kod JS? (wbudowany i zewnętrzny)
  4. Kiedy wykonywany jest CSS (stosowany)?
  5. Kiedy $ (dokument). Już został wykonany?
  6. Czy abc.jpg zostanie pobrany? A może po prostu pobiera kkk.png?

Mam następujące zrozumienie:

  1. Przeglądarka najpierw ładuje HTML (DOM).
  2. Przeglądarka zaczyna ładować zasoby zewnętrzne od góry do dołu, linia po linii.
  3. Jeśli a <script>zostanie spełnione, ładowanie zostanie zablokowane i poczekaj, aż plik JS zostanie załadowany i wykonany, a następnie kontynuuj.
  4. Inne zasoby (CSS / obrazy) są ładowane równolegle i wykonywane w razie potrzeby (np. CSS).

A może tak to wygląda:

Przeglądarka analizuje HTML (DOM) i pobiera zasoby zewnętrzne w formie tablicy lub stosu. Po załadowaniu html przeglądarka zaczyna równolegle ładować zasoby zewnętrzne do struktury i uruchamiać je, dopóki wszystkie zasoby nie zostaną załadowane. Następnie DOM zostanie zmieniony zgodnie z zachowaniami użytkownika w zależności od JS.

Czy ktoś może szczegółowo wyjaśnić, co się stanie, gdy otrzymasz odpowiedź strony HTML? Czy różni się to w różnych przeglądarkach? Wszelkie odniesienia do tego pytania?

Dzięki.

EDYTOWAĆ:

Zrobiłem eksperyment w Firefoksie z Firebug. I pokazuje następujący obraz: alternatywny tekst

Zhu Tao
źródło
11
Steve Souders wykonał wiele pracy w tej dziedzinie. Google dla Steve + Souders + High + Performance i spójrz.
anddoutoi
3
Nie mam na myśli strojenia wydajności. Chcę poznać szczegóły.
Zhu Tao,
2
Po przeczytaniu jego pracy moje zrozumienie tego, jak „to” działa szczegółowo, wzrosło dziesięciokrotnie, więc wciąż jest to ważny komentarz. Prawa autorskie nie zezwalają na cytowanie tutaj całej jego książki, więc wciąż sugeruję, abyś spojrzał na jego prace.
anddoutoi
3
Świetny opis porządku, w którym wszystko się dzieje, znajduje się tutaj
Gerrat

Odpowiedzi:

277

Zgodnie z twoją próbką

<html>
 <head>
  <script src="jquery.js" type="text/javascript"></script>
  <script src="abc.js" type="text/javascript">
  </script>
  <link rel="stylesheets" type="text/css" href="abc.css"></link>
  <style>h2{font-wight:bold;}</style>
  <script>
  $(document).ready(function(){
     $("#img").attr("src", "kkk.png");
  });
 </script>
 </head>
 <body>
    <img id="img" src="abc.jpg" style="width:400px;height:300px;"/>
    <script src="kkk.js" type="text/javascript"></script>
 </body>
</html>

z grubsza wykonanie przebiega w następujący sposób:

  1. Dokument HTML zostanie pobrany
  2. Rozpoczyna się parsowanie dokumentu HTML
  3. Parsowanie HTML sięga <script src="jquery.js" ...
  4. jquery.js jest pobierany i analizowany
  5. Parsowanie HTML sięga <script src="abc.js" ...
  6. abc.js jest pobierany, analizowany i uruchamiany
  7. Parsowanie HTML sięga <link href="abc.css" ...
  8. abc.css jest pobierany i analizowany
  9. Parsowanie HTML sięga <style>...</style>
  10. Wewnętrzne reguły CSS są analizowane i definiowane
  11. Parsowanie HTML sięga <script>...</script>
  12. Wewnętrzny Javascript jest analizowany i uruchamiany
  13. Parsowanie HTML sięga <img src="abc.jpg" ...
  14. abc.jpg jest pobierany i wyświetlany
  15. Parsowanie HTML sięga <script src="kkk.js" ...
  16. kkk.js jest pobierany, analizowany i uruchamiany
  17. Analiza dokumentów HTML kończy się

Pamiętaj, że pobieranie może być asynchroniczne i nieblokujące ze względu na zachowanie przeglądarki. Na przykład w przeglądarce Firefox jest to ustawienie, które ogranicza liczbę jednoczesnych żądań na domenę.

Zależnie od tego, czy komponent został już buforowany, czy nie, może nie zostać ponownie zażądany w najbliższym żądaniu. Jeśli komponent został buforowany, zostanie on załadowany z pamięci podręcznej zamiast z rzeczywistego adresu URL.

Po zakończeniu analizowania, gdy dokument jest gotowy i załadowany, zdarzenia onloadsą uruchamiane. Tak więc po onloadzwolnieniu $("#img").attr("src","kkk.png");jest uruchamiany. Więc:

  1. Dokument jest gotowy, uruchamiane jest ładowanie.
  2. Trafienia wykonania JavaScript $("#img").attr("src", "kkk.png");
  3. kkk.png jest pobierany i ładowany do #img

$(document).ready()Wydarzenie jest rzeczywiście wydarzenie zwolniony, gdy wszystkie elementy strony są załadowane i gotowe. Przeczytaj więcej na ten temat: http://docs.jquery.com/Tutorials:Introducing_$ (dokument) .ready ()

Edycja - ta część omawia bardziej szczegółowo część równoległą lub nie:

Domyślnie iz mojego obecnego stanu wiedzy przeglądarka zwykle uruchamia każdą stronę na 3 sposoby: parser HTML, Javascript / DOM i CSS.

Analizator składni HTML jest odpowiedzialny za parsowanie i interpretację języka znaczników, a zatem musi mieć możliwość wykonywania połączeń z pozostałymi 2 komponentami.

Na przykład, gdy parser natrafi na tę linię:

<a href="#" onclick="alert('test');return false;" style="font-weight:bold">a hypertext link</a>

Analizator składni wykona 3 połączenia, dwa do Javascript i jedno do CSS. Po pierwsze, analizator składni utworzy ten element i zarejestruje go w przestrzeni nazw DOM wraz ze wszystkimi atrybutami związanymi z tym elementem. Po drugie, analizator składni wywoła zdarzenie onclick z tym konkretnym elementem. Na koniec wykona kolejne wywołanie wątku CSS, aby zastosować styl CSS do tego konkretnego elementu.

Wykonanie jest z góry na dół i jednowątkowe. JavaScript może wyglądać na wiele wątków, ale faktem jest, że JavaScript jest jednowątkowy. Dlatego podczas ładowania zewnętrznego pliku javascript parsowanie głównej strony HTML jest zawieszone.

Pliki CSS można jednak pobierać jednocześnie, ponieważ reguły CSS są zawsze stosowane - co oznacza, że ​​elementy są zawsze odświeżane zgodnie z najświeższymi zdefiniowanymi regułami CSS - w ten sposób odblokowując je.

Element będzie dostępny w modelu DOM dopiero po jego przeanalizowaniu. Dlatego podczas pracy z określonym elementem skrypt jest zawsze umieszczany po zdarzeniu onload okna lub w nim.

Taki skrypt spowoduje błąd (w jQuery):

<script type="text/javascript">/* <![CDATA[ */
  alert($("#mydiv").html());
/* ]]> */</script>
<div id="mydiv">Hello World</div>

Ponieważ podczas analizowania skryptu #mydivelement nadal nie jest zdefiniowany. Zamiast tego działałoby:

<div id="mydiv">Hello World</div>
<script type="text/javascript">/* <![CDATA[ */
  alert($("#mydiv").html());
/* ]]> */</script>

LUB

<script type="text/javascript">/* <![CDATA[ */
  $(window).ready(function(){
                    alert($("#mydiv").html());
                  });
/* ]]> */</script>
<div id="mydiv">Hello World</div>
Mauris
źródło
4
dzięki. Ale wspomniałeś, że pobieranie może być asynchroniczne i nieblokujące ze względu na zachowanie przeglądarki , więc jakie elementy można pobrać w asyn (weź FF jako instancję)? <script>zablokuje inne komponenty, prawda? Wszelkie odniesienia do specyfikacji do każdej przeglądarki?
Zhu Tao,
4
$ (dokument) .ready () jest uruchamiany po zakończeniu DOM, a nie po załadowaniu wszystkich składników strony
Pierre
2
@Pierre według składników strony miałem na myśli DOM -> jakiekolwiek elementy w DOM.
Mauris
3
tylko dla wyjaśnienia ... normalne window.onload dzieje się po # 17 ... więc na jakim # jq kod $ (dokument) .ready () działa? # 12? ale sam DOM jest ładowany na pierwszym miejscu, prawda?
armyofda12mnkeys
1
Jeśli na karcie <body> dodamy <link href = "bootstrap.min.css" rel = "stylesheet" /> między tagami <img> i <script>, wówczas obraz img nie jest wyświetlany, dopóki nie zostanie pobrany bootrap ... więc myślę, że krok [13], [14] wymaga modyfikacji ... czy ktoś może wyjaśnić to zachowanie?
Bhuvan
34

1) HTML jest pobierany.

2) HTML jest analizowany stopniowo. Po otrzymaniu żądania zasobu przeglądarka podejmie próbę pobrania zasobu. Domyślną konfiguracją większości serwerów HTTP i większości przeglądarek jest przetwarzanie tylko dwóch żądań równolegle. IE można ponownie skonfigurować tak, aby pobierał nieograniczoną liczbę zasobów równolegle. Steve Souders był w stanie pobrać ponad 100 żądań równolegle na IE. Wyjątkiem jest to, że żądania skryptu blokują równoległe żądania zasobów w IE. Dlatego zaleca się umieszczenie całego JavaScript w zewnętrznych plikach JavaScript i umieszczenie żądania tuż przed tagiem zamykającym ciało w HTML.

3) Po przeanalizowaniu HTML DOM jest renderowany. CSS jest renderowany równolegle z renderowaniem DOM w prawie wszystkich klientach użytkownika. W związku z tym zdecydowanie zaleca się umieszczenie całego kodu CSS w zewnętrznych plikach CSS, które są wymagane jak najwyżej w sekcji <head> </head> dokumentu. W przeciwnym razie strona jest renderowana do wystąpienia pozycji żądania CSS w DOM, a następnie renderowanie rozpoczyna się od góry.

4) Dopiero po całkowitym renderowaniu modelu DOM i rozwiązaniu żądań wszystkich zasobów na stronie lub przekroczeniu limitu czasu JavaScript wykonuje się po zdarzeniu onload. IE7, i nie jestem pewien co do IE8, nie przeterminowuje zasobów szybko, jeśli odpowiedź HTTP nie zostanie odebrana z żądania zasobu. Oznacza to, że zasób żądany przez JavaScript bezpośrednio na stronie, czyli JavaScript zapisany w tagach HTML, który nie jest zawarty w funkcji, może uniemożliwić wykonanie zdarzenia onload przez wiele godzin. Ten problem można uruchomić, jeśli taki wbudowany kod istnieje na stronie i nie zostanie wykonany z powodu kolizji przestrzeni nazw, która powoduje awarię kodu.

Z powyższych kroków najbardziej obciążającym CPU jest parsowanie DOM / CSS. Jeśli chcesz, aby twoja strona była przetwarzana szybciej, napisz efektywny CSS, eliminując zbędne instrukcje i konsolidując instrukcje CSS w jak najmniejszej liczbie odwołań do elementów. Zmniejszenie liczby węzłów w drzewie DOM spowoduje również szybsze renderowanie.

Pamiętaj, że każdy zasób, o który prosisz z kodu HTML, a nawet z zasobów CSS / JavaScript, jest wymagany z osobnym nagłówkiem HTTP. To zużywa przepustowość i wymaga przetwarzania na żądanie. Jeśli chcesz, aby strona ładowała się tak szybko, jak to możliwe, zmniejsz liczbę żądań HTTP i zmniejsz rozmiar HTML. Użytkownikowi nie sprzyja żadna przysługa, uśredniając wagę strony na poziomie 180 tys. Od samego HTML. Wielu programistów zgadza się z pewnym błędem, że użytkownik decyduje o jakości treści na stronie w ciągu 6 nanosekund, a następnie usuwa zapytanie DNS z serwera i spala komputer, jeśli jest niezadowolony, więc zamiast tego zapewniają najpiękniejszą stronę na 250k HTML. Zadbaj o krótki i słodki kod HTML, aby użytkownik mógł szybciej ładować strony.


źródło
2
konsolidacja instrukcji CSS w jak najmniejszej liczbie odwołań do elementów Brzmi dziwnie. Jeśli muszę stylizować trzy elementy, muszę odwoływać się dokładnie do trzech elementów. Nie mogę nawiązać do stylu dziesięciu, prawda? Lub rozwinąć na ten temat
Green
12

Otwórz swoją stronę w przeglądarce Firefox i pobierz dodatek HTTPFox. Powie ci wszystko, czego potrzebujesz.

Znaleziono to na archivist.incuito:

http://archivist.incutio.com/viewlist/css-discuss/76444

Podczas pierwszego żądania strony przeglądarka wysyła żądanie GET na serwer, który zwraca kod HTML do przeglądarki. Przeglądarka następnie rozpoczyna analizowanie strony (być może zanim wszystkie zostaną zwrócone).

Gdy znajdzie odniesienie do zewnętrznego obiektu, takiego jak plik CSS, plik obrazu, plik skryptu, plik Flash lub cokolwiek innego poza stroną (na tym samym serwerze / domenie lub nie), przygotowuje się do wykonania kolejne żądanie GET dla tego zasobu.

Jednak standard HTTP określa, że ​​przeglądarka nie powinna wysyłać więcej niż dwóch równoczesnych żądań do tej samej domeny. Umieszcza więc każde żądanie w określonej domenie w kolejce, a po zwróceniu każdej jednostki rozpoczyna kolejną w kolejce dla tej domeny.

Czas potrzebny na zwrócenie encji zależy od jej wielkości, obciążenia, którego aktualnie doświadcza serwer, oraz aktywności każdej maszyny między maszyną z przeglądarką a serwerem. Lista tych maszyn może w zasadzie być inna dla każdego żądania, do tego stopnia, że ​​jeden obraz może zostać przesłany z USA do mnie w Wielkiej Brytanii przez Atlantyk, podczas gdy inny z tego samego serwera wychodzi przez Pacyfik, Azję i Europę, co trwa dłużej. Możesz więc otrzymać następującą sekwencję, w której strona ma (w tej kolejności) odwołania do trzech plików skryptów i pięciu plików obrazów, wszystkie o różnych rozmiarach:

  1. GET script1 i script2; żądanie kolejki dla script3 i images1-5.
  2. przybywa skrypt2 (jest mniejszy niż skrypt1): GET skrypt3, obrazy w kolejce 1-5.
  3. skrypt1 przybywa; GET image1, obrazy kolejki2-5.
  4. przybywa obraz1, GET obraz2, obrazy kolejki3-5.
  5. skrypt3 nie dotarł z powodu problemu z siecią - GET script3 ponownie (automatyczne ponawianie).
  6. obraz 2 przybywa, skrypt3 wciąż nie ma; GET image3, obrazy w kolejce4-5.
  7. obraz 3 przybywa; GET image4, kolejka image5, skrypt3 wciąż w drodze.
  8. przybywa obraz 4, GET obraz 5;
  9. obraz 5 przybywa.
  10. skrypt3 przybywa.

W skrócie: każda stara kolejność, w zależności od tego, co robi serwer, co robi reszta Internetu i czy cokolwiek zawiera błędy i wymaga ponownego pobrania. Może to wydawać się dziwnym sposobem robienia rzeczy, ale dosłownie niemożliwe byłoby, aby Internet (nie tylko WWW) działał z jakimkolwiek stopniem niezawodności, gdyby nie zostało to zrobione w ten sposób.

Ponadto wewnętrzna kolejka przeglądarki może nie pobierać encji w kolejności, w jakiej pojawiają się na stronie - nie jest to wymagane przez żaden standard.

(Och, i nie zapomnij buforować, zarówno w przeglądarce, jak i w buforowaniu serwerów proxy używanych przez dostawców usług internetowych w celu zmniejszenia obciążenia sieci).

tahdhaze09
źródło
6

Jeśli pytasz o to, ponieważ chcesz przyspieszyć swoją stronę internetową, sprawdź stronę Yahoo na temat najlepszych praktyk przyśpieszania swojej strony internetowej . Zawiera wiele sprawdzonych metod przyspieszania witryny.

płatny kujon
źródło
2

AFAIK, przeglądarka (przynajmniej Firefox) żąda każdego zasobu, gdy tylko go przeanalizuje. Jeśli napotka tag img, zażąda tego obrazu, jak tylko parsowany zostanie tag img. I to może być nawet zanim otrzyma całość dokumentu HTML ... to znaczy, że nadal może to być pobieranie dokumentu HTML, kiedy to się stanie.

W przypadku przeglądarki Firefox obowiązują kolejki przeglądarki, w zależności od tego, jak są ustawione w about: config. Na przykład nie będzie próbował pobrać więcej niż 8 plików jednocześnie z tego samego serwera ... dodatkowe żądania zostaną umieszczone w kolejce. Sądzę, że istnieją limity na domenę, na proxy i inne rzeczy, które są udokumentowane na stronie Mozilli i można je ustawić w about: config. Czytałem gdzieś, że IE nie ma takich ograniczeń.

Zdarzenie gotowości jQuery jest uruchamiane, gdy tylko główny dokument HTML zostanie pobrany i parsowany DOM. Następnie zdarzenie ładowania jest uruchamiane po pobraniu i przeanalizowaniu wszystkich połączonych zasobów (CSS, obrazy itp.). Jest to wyjaśnione w dokumentacji jQuery.

Jeśli chcesz kontrolować kolejność, w której wszystko jest ładowane, uważam, że najbardziej wiarygodnym sposobem jest JavaScript.

Rolf
źródło
1

Dynatrace AJAX Edition pokazuje dokładną sekwencję ładowania, analizowania i wykonywania strony.

Chetan S.
źródło
1

Wygląda na to, że wybrana odpowiedź nie dotyczy nowoczesnych przeglądarek, przynajmniej w przeglądarce Firefox 52. Zauważyłem, że żądania ładowania zasobów takich jak css, javascript są wydawane, zanim parser HTML dotrze do elementu, na przykład

<html>
  <head>
    <!-- prints the date before parsing and blocks HTMP parsering -->
    <script>
      console.log("start: " + (new Date()).toISOString());
      for(var i=0; i<1000000000; i++) {};
    </script>

    <script src="jquery.js" type="text/javascript"></script>
    <script src="abc.js" type="text/javascript"></script>
    <link rel="stylesheets" type="text/css" href="abc.css"></link>
    <style>h2{font-wight:bold;}</style>
    <script>
      $(document).ready(function(){
      $("#img").attr("src", "kkk.png");
     });
   </script>
 </head>
 <body>
   <img id="img" src="abc.jpg" style="width:400px;height:300px;"/>
   <script src="kkk.js" type="text/javascript"></script>
   </body>
</html>

Odkryłem, że czas rozpoczęcia żądań załadowania zasobów css i javascript nie był blokowany. Wygląda na to, że Firefox ma skan HTML i identyfikuje kluczowe zasoby (zasób img nie jest dołączony) przed rozpoczęciem analizowania HTML.

Xiaoming
źródło