Czy CSS powinien zawsze wyprzedzać Javascript?

897

W niezliczonych miejscach online widziałem zalecenie włączenia CSS przed JavaScript. Rozumowanie ma następującą postać :

Jeśli chodzi o zamawianie CSS i JavaScript, chcesz, aby Twój CSS był na pierwszym miejscu. Powodem jest to, że wątek renderujący zawiera wszystkie informacje o stylu potrzebne do renderowania strony. Jeśli JavaScript obejmuje najpierw, silnik JavaScript musi go przeanalizować, zanim przejdzie do następnego zestawu zasobów. Oznacza to, że wątek renderujący nie może całkowicie wyświetlić strony, ponieważ nie ma wszystkich potrzebnych stylów.

Moje rzeczywiste testy ujawniają coś zupełnie innego:

Moja uprząż testowa

Używam następującego skryptu Ruby, aby wygenerować określone opóźnienia dla różnych zasobów:

require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
require 'date'

class Handler  < EventMachine::Connection
  include EventMachine::HttpServer

  def process_http_request
    resp = EventMachine::DelegatedHttpResponse.new( self )

    return unless @http_query_string

    path = @http_path_info
    array = @http_query_string.split("&").map{|s| s.split("=")}.flatten
    parsed = Hash[*array]

    delay = parsed["delay"].to_i / 1000.0
    jsdelay = parsed["jsdelay"].to_i

    delay = 5 if (delay > 5)
    jsdelay = 5000 if (jsdelay > 5000)

    delay = 0 if (delay < 0) 
    jsdelay = 0 if (jsdelay < 0)

    # Block which fulfills the request
    operation = proc do
      sleep delay 

      if path.match(/.js$/)
        resp.status = 200
        resp.headers["Content-Type"] = "text/javascript"
        resp.content = "(function(){
            var start = new Date();
            while(new Date() - start < #{jsdelay}){}
          })();"
      end
      if path.match(/.css$/)
        resp.status = 200
        resp.headers["Content-Type"] = "text/css"
        resp.content = "body {font-size: 50px;}"
      end
    end

    # Callback block to execute once the request is fulfilled
    callback = proc do |res|
        resp.send_response
    end

    # Let the thread pool (20 Ruby threads) handle request
    EM.defer(operation, callback)
  end
end

EventMachine::run {
  EventMachine::start_server("0.0.0.0", 8081, Handler)
  puts "Listening..."
}

Powyższy mini serwer pozwala mi ustawić dowolne opóźnienia dla plików JavaScript (zarówno serwera, jak i klienta) oraz dowolne opóźnienia CSS. Na przykład http://10.0.0.50:8081/test.css?delay=500daje mi 500 ms opóźnienie przesyłania CSS.

Używam następującej strony do testowania.

<!DOCTYPE html>
<html>
  <head>
      <title>test</title>
      <script type='text/javascript'>
          var startTime = new Date();
      </script>
      <link href="http://10.0.0.50:8081/test.css?delay=500" type="text/css" rel="stylesheet">
      <script type="text/javascript" src="http://10.0.0.50:8081/test2.js?delay=400&amp;jsdelay=1000"></script> 
  </head>
  <body>
    <p>
      Elapsed time is: 
      <script type='text/javascript'>
        document.write(new Date() - startTime);
      </script>
    </p>    
  </body>
</html>

Gdy najpierw dołączę CSS, wyświetlenie strony zajmuje 1,5 sekundy:

Najpierw CSS

Gdy najpierw dołączę kod JavaScript, wyświetlenie strony zajmuje 1,4 sekundy:

JavaScript pierwszy

Podobne wyniki uzyskuję w Chrome, Firefox i Internet Explorer. Jednak w Operze kolejność po prostu nie ma znaczenia.

Wydaje się, że dzieje się tak, że interpreter JavaScript odmawia uruchomienia, dopóki cały CSS nie zostanie pobrany. Wygląda więc na to, że włączenie JavaScript jako pierwszego jest bardziej wydajne, ponieważ wątek JavaScript ma dłuższy czas działania.

Czy coś pomijam, czy zalecenie umieszczania CSS przed JavaScript włącza jest nieprawidłowe?

Oczywiste jest, że możemy dodać asynchronię lub użyć setTimeout, aby zwolnić wątek renderujący lub umieścić kod JavaScript w stopce, lub użyć modułu ładującego JavaScript. Chodzi o to, aby uporządkować niezbędne bity JavaScript i bity CSS w głowie.

Sam Saffron
źródło
120
czy 1511 vs. 1422 jest statystycznie istotną różnicą? To 6 procent. Ogólny próg istotnej do przeciętnej różnicy w wydajności u ludzi wynosi około 20 procent.
Jeff Atwood
15
Chodzi o to, że zmiana kolejności eliminuje to dowolne opóźnienie, możesz ustawić opóźnienie na dowolne, tylko demonstracja problemu.
Sam Saffron,
3
czy twoje opóźnienie było 100ms? różnica na twoich zrzutach ekranu wynosi 89ms. W twoim adresie URL jest delay=400&amp;jsdelay=1000i delay=500który nie jest w pobliżu 100ms lub 89ms. Chyba nie jestem pewien, do jakich liczb się odnosisz.
Jeff Atwood
4
„Jeśli JavaScript obejmuje najpierw, silnik JavaScript musi go przeanalizować, zanim przejdzie do następnego zestawu zasobów. Oznacza to, że wątek renderujący nie może całkowicie wyświetlić strony, ponieważ nie ma wszystkich potrzebnych stylów . ” - jeśli dołączenie JS znajduje się w głowie, wówczas JS zostanie wykonany przed renderowaniem strony, niezależnie od tego, czy dołączenie JS było przed czy po.
nnnnnn
162
Nie jestem pewien, czy to rozważyłeś, ale ważne jest również postrzeganie czasu ładowania. Na przykład, jeśli ładowanie CSS najpierw daje ci nawet kolor tła / teksturę strony, wydaje się, że jest szybsze. Bezwzględne czasy ładowania mogą tego nie wskazywać.
Rakesh Pai

Odpowiedzi:

712

To bardzo interesujące pytanie. Zawsze stawiam CSS <link href="...">przed JS, <script src="...">ponieważ „Pewnego razu przeczytałem, że jest lepiej”. Masz rację; najwyższy czas, abyśmy przeprowadzili jakieś rzeczywiste badania!

Skonfigurowałem własną wiązkę testową w węźle (kod poniżej). Zasadniczo ja:

  • Upewnij się, że nie ma buforowania HTTP, więc przeglądarka będzie musiała wykonać pełne pobieranie przy każdym załadowaniu strony.
  • Aby symulować rzeczywistość, dołączyłem jQuery i CSS H5BP (więc jest spora ilość skryptu / CSS do przeanalizowania)
  • Skonfiguruj dwie strony - jedną z CSS przed skryptem, drugą z CSS po skrypcie.
  • Rejestrowane, jak długo zajęło skryptu zewnętrznego w <head>celu wykonania
  • Zarejestrowano, ile czasu zajęło wykonanie skryptu wbudowanego w programie <body>, co jest analogiczne do DOMReady.
  • Opóźnione przesłanie CSS i / lub skryptu do przeglądarki o 500ms.
  • Przebiegł test 20 razy w 3 głównych przeglądarkach.

Wyniki

Po pierwsze, plik CSS jest opóźniony o 500 ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 583ms  36ms  | 559ms  42ms  | 565ms 49ms
St Dev      | 15ms   12ms  | 9ms    7ms   | 13ms  6ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 584ms  521ms | 559ms  513ms | 565ms 519ms
St Dev      | 15ms   9ms   | 9ms    5ms   | 13ms  7ms

Następnie ustawiam jQuery na opóźnienie o 500 ms zamiast CSS:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 597ms  556ms | 562ms  559ms | 564ms 564ms
St Dev      | 14ms   12ms  | 11ms   7ms   | 8ms   8ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 598ms  557ms | 563ms  560ms | 564ms 565ms
St Dev      | 14ms   12ms  | 10ms   7ms   | 8ms   8ms

Wreszcie mogę ustawić zarówno jQuery i CSS opóźnić przez 500ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 620ms  560ms | 577ms  577ms | 571ms 567ms
St Dev      | 16ms   11ms  | 19ms   9ms   | 9ms   10ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 623ms  561ms | 578ms  580ms | 571ms 568ms
St Dev      | 18ms   11ms  | 19ms   9ms   | 9ms   10ms

Wnioski

Po pierwsze, należy zauważyć, że działam przy założeniu, że masz skrypty znajdujące się w <head>twoim dokumencie (w przeciwieństwie do końca <body>). Istnieją różne argumenty dotyczące tego, dlaczego możesz linkować do swoich skryptów w <head>przeciwieństwie do końca dokumentu, ale to nie wchodzi w zakres tej odpowiedzi. Jest to ściśle związane z tym, czy <script>s powinien iść przed <link>s <head>.

W nowoczesnych przeglądarkach DESKTOP wygląda na to , że najpierw połączenie z CSS nigdy nie zapewnia wzrostu wydajności. Umieszczenie CSS po skrypcie daje trywialną korzyść, gdy zarówno CSS, jak i skrypt są opóźnione, ale daje duże korzyści, gdy CSS jest opóźniony. (Pokazane przez lastkolumny w pierwszym zestawie wyników).

Biorąc pod uwagę, że łączenie z CSS jako ostatnie nie wydaje się obniżać wydajności, ale może przynieść korzyści w pewnych okolicznościach, powinieneś połączyć się z zewnętrznymi arkuszami stylów po połączeniu z zewnętrznymi skryptami tylko w przeglądarkach komputerowych, jeśli wydajność starych przeglądarek nie stanowi problemu. Czytaj dalej na temat sytuacji mobilnej.

Dlaczego?

Historycznie, gdy przeglądarka napotkała <script>znacznik wskazujący na zewnętrzny zasób, przeglądarka przestałaby parsować HTML, odzyskał skrypt, uruchomił go, a następnie kontynuował parsowanie HTML. W przeciwieństwie do tego, jeśli przeglądarka napotkała <link>zewnętrzny arkusz stylów, kontynuowałaby parsowanie HTML podczas pobierania pliku CSS (równolegle).

Dlatego często powtarzane porady dotyczące umieszczania arkuszy stylów na pierwszym miejscu - najpierw były pobierane, a pierwszy skrypt do pobrania można ładować równolegle.

Jednak współczesne przeglądarki (w tym wszystkie przeglądarki, które testowałem powyżej) wdrożyły spekulacyjne parsowanie , w którym przeglądarka „patrzy w przyszłość” w HTML i zaczyna pobierać zasoby przed pobraniem i uruchomieniem skryptów.

W starych przeglądarkach bez spekulacyjnego analizowania umieszczanie skryptów na pierwszym miejscu wpływa na wydajność, ponieważ nie będą one pobierane równolegle.

Obsługa przeglądarki

Analiza spekulacyjna została po raz pierwszy zaimplementowana w: (wraz z odsetkiem użytkowników przeglądarek komputerowych na całym świecie korzystających z tej wersji lub nowszej od stycznia 2012 r.)

  • Chrome 1 (WebKit 525) (100%)
  • IE 8 (75%)
  • Firefox 3.5 (96%)
  • Safari 4 (99%)
  • Opera 11.60 (85%)

W sumie około 85% obecnie używanych przeglądarek stacjonarnych obsługuje ładowanie spekulacyjne. Umieszczenie skryptów przed CSS spowoduje obniżenie wydajności 15% użytkowników na całym świecie ; YMMV na podstawie określonych odbiorców witryny. (I pamiętaj, że liczba maleje).

W przeglądarkach mobilnych uzyskiwanie ostatecznych liczb jest nieco trudniejsze ze względu na niejednorodność przeglądarki mobilnej i systemu operacyjnego. Ponieważ renderowanie spekulatywne zostało zaimplementowane w WebKit 525 (wydanym w marcu 2008 r.) I prawie każda wartościowa przeglądarka mobilna oparta jest na WebKit, możemy stwierdzić, że „większość” przeglądarek mobilnych powinna go obsługiwać. Zgodnie z quirksmode , iOS 2.2 / Android 1.0 używa WebKit 525. Nie mam pojęcia, jak wygląda Windows Phone.

Jednak uruchomiłem test na moim urządzeniu z Androidem 4 i chociaż zobaczyłem liczby podobne do wyników na pulpicie, podłączyłem go do fantastycznego nowego zdalnego debuggera w Chrome na Androida, a karta Sieć pokazała, że ​​przeglądarka faktycznie czeka na pobranie CSS, aż JavaScript całkowicie się załaduje - innymi słowy, nawet najnowsza wersja WebKit na Androida nie obsługuje parsowania spekulacyjnego. Podejrzewam, że można go wyłączyć z powodu ograniczeń procesora, pamięci i / lub sieci nieodłącznie związanych z urządzeniami mobilnymi.

Kod

Wybacz niechlujstwo - to były pytania i odpowiedzi.

app.js

var express = require('express')
, app = express.createServer()
, fs = require('fs');

app.listen(90);

var file={};
fs.readdirSync('.').forEach(function(f) {
    console.log(f)
    file[f] = fs.readFileSync(f);
    if (f != 'jquery.js' && f != 'style.css') app.get('/' + f, function(req,res) {
        res.contentType(f);
        res.send(file[f]);
    });
});


app.get('/jquery.js', function(req,res) {
    setTimeout(function() {
        res.contentType('text/javascript');
        res.send(file['jquery.js']);
    }, 500);
});

app.get('/style.css', function(req,res) {
    setTimeout(function() {
        res.contentType('text/css');
        res.send(file['style.css']);
    }, 500);
});


var headresults={
    css: [],
    js: []
}, bodyresults={
    css: [],
    js: []
}
app.post('/result/:type/:time/:exec', function(req,res) {
    headresults[req.params.type].push(parseInt(req.params.time, 10));
    bodyresults[req.params.type].push(parseInt(req.params.exec, 10));
    res.end();
});

app.get('/result/:type', function(req,res) {
    var o = '';
    headresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    o+='\n';
    bodyresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    res.send(o);
});

css.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <link rel="stylesheet" href="style.css">
        <script src="jquery.js"></script>
        <script src="test.js"></script>
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

js.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <script src="jquery.js"></script>
        <script src="test.js"></script>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

test.js

var jsload = Date.now();


$(function() {
    $.post('/result' + location.pathname.replace('.html','') + '/' + (jsload - start) + '/' + (bodyexec - start));
});

Plik jquery.js to jquery-1.7.1.min.js

josh3736
źródło
137
to fantastyczna odpowiedź, dziękuję za wykorzystanie nauki! Według twojego wyniku „w nowoczesnych przeglądarkach wygląda na to, że najpierw link do CSS nigdy nie zapewnia wzrostu wydajności” , myślę, że odpowiedź na tytuł pytania brzmi „ tak” , stara rada najpierw CSS jest wyraźnie nieprawidłowa.
Jeff Atwood
Jeśli chodzi o aktualizację @ josh3736 dotyczącą odwrotności na urządzeniach mobilnych ... jest to przypadek, aby nie rzucać pistoletem na tę znaczącą zmianę. Byłbym ciekawy, jak zachowują się inne przeglądarki mobilne (webkit, gecko, presto, trident itp.), Ponieważ wydajność na urządzeniach mobilnych jest często ważniejsza.
scunliffe
1
Powinieneś także spróbować dodać trochę powolności do drukowania css / js, aby zasymulować prędkość wolnego serwera.
kirb
1
„Po pierwsze, ważne jest, aby pamiętać, że działam przy założeniu, że masz skrypty znajdujące się w <head>twoim dokumencie (w przeciwieństwie do końca <body>).” Chciałbym podkreślić to znacznie wcześniej w odpowiedzi, jak na górze. Umieszczenie scriptw headpliku, który odnosi się do zewnętrznego pliku, prawie nigdy nie jest poprawne, z prawie każdej perspektywy (na pewno nie z punktu widzenia wydajności). Nie przypominam sobie, żebym kiedykolwiek musiał to robić w prawdziwym życiu. Może dziwna linia lub dwa wbudowanego skryptu, ale to wszystko. Domyślnym, bez bardzo dobrych przeciwnych powodów, powinien być koniec ciała.
TJ Crowder,
1
Obniżone głosowanie, jQuery nie jest JavaScriptem, a jedynie rozszerza stronę, dlatego wyniki przy jego użyciu nie są obiektywne.
John
303

Istnieją dwa główne powody, aby umieścić CSS przed JavaScript.

  1. Stare przeglądarki (Internet Explorer 6-7, Firefox 2 itp.) Blokowałyby wszystkie kolejne pobrania, gdy zaczęły pobierać skrypt. Więc jeśli a.jsnastępnie b.css, zostaną pobrane sekwencyjnie: najpierw a, a następnie b. Jeśli b.cssnastępnie a.jsdostają pobrane równolegle tak załadowaniu strony szybciej.

  2. Nic nie jest renderowane, dopóki nie zostaną pobrane wszystkie arkusze stylów - dotyczy to wszystkich przeglądarek. Skrypty są różne - blokują renderowanie wszystkich elementów DOM znajdujących się poniżej znacznika script na stronie. Jeśli umieścisz swoje skrypty w HEAD, oznacza to, że cała strona nie będzie renderowana do momentu pobrania wszystkich arkuszy stylów i wszystkich skryptów. Chociaż sensowne jest blokowanie renderowania wszystkich arkuszy stylów (aby uzyskać poprawne stylizowanie za pierwszym razem i uniknąć flashowania niestylizowanej zawartości FOUC), nie ma sensu blokowanie renderowania całej strony dla skryptów. Często skrypty nie wpływają na żadne elementy DOM lub tylko część elementów DOM. Najlepiej jest ładować skrypty jak najniżej na stronie, a nawet ładować je asynchronicznie.

Tworzenie przykładów za pomocą Cuzillion jest fajne . Na przykład ta strona ma skrypt w HEAD, więc cała strona jest pusta, dopóki pobieranie nie zostanie zakończone. Jeśli jednak przeniesiemy skrypt na koniec bloku BODY, renderowany jest nagłówek strony, ponieważ te elementy DOM występują powyżej znacznika SCRIPT, jak widać na tej stronie .

Steve Souders
źródło
10
Uhonorowany Steve pobił mnie do odpowiedzi, ale dodam artykuł odpowiedni do tego, o czym wspomina: stevesouders.com/blog/2009/04/27/...
Juan Pablo Buritica
4
zobacz, które przeglądarki obsługują asyncatrybut, który Steve zaleca tutaj, gdy mówi „jeszcze lepiej ładuj je asynchronicznie” - stackoverflow.com/questions/1834077/...
Jeff Atwood
Hej, możesz mi powiedzieć, dlaczego ktoś miałby łączyć pliki CSS z @importdyrektywami?
Josh Stodola
6
Jakie jest źródło 2), a jeśli to prawda, czy możesz następnie wyjaśnić, dlaczego czasami strona kończy ładowanie zawartości, a następnie CSS stosuje się sekundę lub dwie później? (
Zdarza
2
Więc powinniśmy umieścić jQuery+ jQuery UI+ $(document).ready(function () { });na końcu strony? Czy zawsze będzie działać zgodnie z oczekiwaniami?
Olivier Pons,
42

Nie podkreśliłbym zbytnio wyników, które osiągnąłeś, uważam, że jest to subiektywne, ale mam powód, aby wyjaśnić, że lepiej jest umieścić CSS przed js.

Podczas wczytywania witryny istnieją dwa scenariusze:

PRZYPADEK 1: biały ekran> strona bez stylu> strona w stylu> interakcja> strona w stylu i interaktywna

PRZYKŁAD 2: biały ekran> strona bez stylu> interakcja> strona w stylu> strona w stylu i interaktywna


Szczerze nie wyobrażam sobie, żeby ktoś wybrał Przypadek 2. To by znaczyło odwiedzający korzystający z powolnych połączeń internetowych będą mieli do czynienia z niestylizowaną witryną, która pozwoli im na interakcję z wykorzystaniem Javascript (ponieważ jest już załadowana). Co więcej, czas spędzony na przeglądaniu niestylizowanej witryny zostałby zmaksymalizowany w ten sposób. Dlaczego ktoś miałby tego chcieć?

Działa także lepiej, jak stwierdza jQuery

„Podczas korzystania ze skryptów opartych na wartości właściwości stylu CSS ważne jest, aby odwoływać się do zewnętrznych arkuszy stylów lub osadzać elementy stylów przed odwołaniem się do skryptów”.

Gdy pliki są ładowane w niewłaściwej kolejności (najpierw JS, a następnie CSS), kod JavaScript oparty na właściwościach ustawionych w plikach CSS (na przykład szerokość lub wysokość div) nie zostanie poprawnie załadowany. Wygląda na to, że przy niewłaściwej kolejności ładowania właściwe właściwości są „czasami” znane JavaScript (być może jest to spowodowane warunkami wyścigu?). Ten efekt wydaje się większy lub mniejszy w zależności od używanej przeglądarki.

defau1t
źródło
2
W jaki sposób zabrałbyś się do zagwarantowania, że ​​wszystkie css zostały załadowane przed uruchomieniem javascript? Czy możesz? lub czy Twój skrypt javascript powinien być wystarczająco solidny, aby poradzić sobie z sytuacją, w której style niekoniecznie zostaną załadowane.
Jonnio
@Jonnio Jeśli twoja JS ma zależność, powinieneś ją wyraźnie zaznaczyć. W przeciwnym razie zawsze będziesz mieć rzadkie problemy z synchronizacją. Moduły ES6 są dobrym sposobem, aby tak się stało, ale istnieje wiele bibliotek, które można również wykorzystać.
kmkemp
26

Czy Twoje testy zostały przeprowadzone na komputerze osobistym, czy na serwerze WWW? To pusta strona, czy może złożony system online z obrazami, bazami danych itp.? Czy Twoje skrypty wykonują prostą akcję zdarzenia najechania kursorem, czy też są kluczowym składnikiem sposobu renderowania witryny i interakcji z użytkownikiem? Jest tu kilka rzeczy do rozważenia, a trafność tych zaleceń prawie zawsze staje się regułami, gdy zapuszczasz się do tworzenia wysokiej jakości stron internetowych.

Celem reguły „umieszczanie arkuszy stylów na górze i skryptów na dole” jest to, że ogólnie jest to najlepszy sposób na osiągnięcie optymalnego renderowania progresywnego , co ma kluczowe znaczenie dla wygody użytkownika.

Wszystko inne na bok: zakładając, że test jest ważny, a wyniki naprawdę są sprzeczne z popularnymi zasadami, nie byłoby to niczym zaskakującym. Każda strona internetowa (i wszystko, czego potrzeba, aby całość pojawiła się na ekranie użytkownika) jest inna, a Internet stale się rozwija.

niedzielę
źródło
1
Doceniam pogrubiony punkt, ale OP mówi o tym, co się stanie, gdy zmienisz kolejność, tak aby oba były na górze, ani na dole.
nnnnnn
1
Zatem „zakładając, że [jego] test jest ważny”.
skippr
21

Załączam pliki CSS przed Javascriptem z innego powodu.

Jeśli mój Javascript musi dokonać dynamicznej zmiany rozmiaru jakiegoś elementu strony (w tych narożnych przypadkach, w których CSS jest naprawdę głównym z tyłu), wówczas ładowanie CSS po zrobieniu JS może prowadzić do warunków wyścigu, w których rozmiar elementu jest zmieniany przed stylami CSS są stosowane i dlatego wyglądają dziwnie, kiedy style wreszcie się uruchomią. Jeśli załaduję CSS wcześniej, mogę zagwarantować, że wszystko działa w zamierzonej kolejności i że ostateczny układ jest taki, jaki chcę.

hugomg
źródło
2
spowoduje to uszkodzenie jednego dnia w niektórych przeglądarkach. Nie zgaduję.
jcolebrand
1
jcolebrand: Tak, myślę, że nie wypiłem wystarczająco dużo kawy, kiedy to napisałem. (Patrząc wstecz, myślę, że ważne jest, aby uniknąć dynamicznego ładowania CSS i umieszczenia JS w zdarzeniu domReady, jeśli chcesz dokonać dynamicznego sortowania)
hugomg
Skrypty nie powinny zmieniać żadnego wyświetlania. To jest praca CSS. HTML = treść, CSS = Jak wyświetlać zawartość, javascript zmienia zawartość dynamicznie. Również js powinien działać tylko po (lub podczas) uruchomienia DOMContentLoaded w niektórych małych, ale bardzo specyficznych sytuacjach.
brunoais,
@brunoais: Niektóre układy można jednak tworzyć tylko za pomocą Javascript. Na przykład wszystko, co wymaga dynamicznej zmiany rozmiaru, musi być wykonane za pomocą Javascript, a niektóre rzeczy (np. Mające rozmiar 100% - 20px) wymagają Javascript, aby można je było przenosić w starych przeglądarkach.
hugomg,
@missingno W takich przypadkach po prostu skorzystaj ze zdarzenia DOMContentLoaded. Ale rozumiem, co masz na myśli. (Głupi IE!)
brunoais,
10

Czy zalecenie dołączenia CSS przed JavaScript jest nieprawidłowe?

Nie, jeśli traktujesz to jako zwykłą rekomendację. Ale jeśli traktujesz to jako twardą i szybką zasadę ?, tak, to jest nieważne.

From https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded

Arkusz stylów ładuje blokowanie wykonywania skryptu, więc jeśli masz <script> później, <link rel="stylesheet" ...>strona nie zakończy parsowania - a DOMContentLoaded nie uruchomi się - dopóki arkusz stylów nie zostanie załadowany.

Wygląda na to, że musisz wiedzieć, na czym polega każdy skrypt i upewnić się, że wykonanie skryptu jest opóźnione do momentu właściwego zdarzenia zakończenia. Jeśli skrypt opiera się tylko na modelu DOM, można go wznowić w pliku ondomready / domcontentloaded, jeśli zależy on od obrazów do załadowania lub arkuszy stylów do zastosowania, to jeśli poprawnie odczytam powyższe odniesienie, kod ten musi zostać odroczony do momentu zdarzenia onload.

Nie sądzę, aby jeden rozmiar skarpety pasował do wszystkich, mimo że tak właśnie są sprzedawane i wiem, że jeden rozmiar buta nie pasuje do wszystkich. Nie sądzę, że istnieje ostateczna odpowiedź, do której należy załadować najpierw style lub skrypt. Jest to bardziej indywidualna decyzja, co należy załadować w jakiej kolejności, a co można odłożyć na później, ponieważ nie znajduje się na „ścieżce krytycznej”.

Aby porozmawiać z obserwatorem, który skomentował, że lepiej opóźnić możliwość interakcji użytkownika, aż arkusz będzie ładny. Jest wielu z was i irytujecie swoich partnerów, którzy czują coś wręcz przeciwnego. Przybyli na stronę, aby osiągnąć cel, a opóźnienia w ich interakcji z witryną podczas oczekiwania na rzeczy, które nie mają znaczenia, aby zakończyć ładowanie, są bardzo frustrujące. Nie twierdzę, że się mylisz, tylko powinieneś mieć świadomość, że istnieje inna frakcja, która nie podziela twojego priorytetu.

To pytanie dotyczy w szczególności wszystkich reklam umieszczanych na stronach internetowych. Bardzo bym chciał, gdyby autorzy witryny renderowali tylko elementy zastępcze div dla treści reklamy i upewniali się, że ich strona została załadowana i interaktywna przed wstrzyknięciem reklam w zdarzenie obciążenia. Nawet wtedy chciałbym, aby reklamy były ładowane szeregowo zamiast wszystkich naraz, ponieważ mają one wpływ na moją zdolność do przewijania zawartości witryny podczas ładowania rozdętych reklam. Ale to tylko punkt widzenia jednej osoby.

  • Poznaj swoich użytkowników i ich wartości.
  • Poznaj swoich użytkowników i używanego środowiska przeglądania.
  • Dowiedz się, co robi każdy plik i jakie są jego wymagania wstępne. Sprawienie, by wszystko działało, będzie miało pierwszeństwo przed szybkością i ładnością.
  • Podczas opracowywania używaj narzędzi wyświetlających linię czasu sieci.
  • Przetestuj w każdym środowisku używanym przez użytkowników. Może być konieczne dynamiczne (po stronie serwera, podczas tworzenia strony) zmienianie kolejności ładowania w zależności od środowiska użytkownika.
  • W razie wątpliwości zmień kolejność i zmierz ponownie.
  • Możliwe, że mieszanie stylów i skryptów w kolejności ładowania będzie optymalne; nie wszystko jedno, a nie drugie.
  • Eksperymentuj nie tylko w jakiej kolejności ładować pliki, ale gdzie. Głowa? W ciele? Po ciele? DOM gotowy / załadowany? Załadowany?
  • Rozważ opcje asynchroniczne i odroczenia, gdy jest to właściwe, aby zmniejszyć opóźnienie netto, które wystąpi przed użyciem strony. Przetestuj, aby ustalić, czy pomagają, czy ranią.
  • Zawsze będą kompromisy do rozważenia przy ocenie optymalnej kolejności ładowania. Dość kontra responsywne bycie jednym.
Ted Cohen
źródło
1
Powiązany artykuł nie twierdzi już „Arkusz stylów ładuje blokowanie wykonywania skryptu”. Czy to już nie prawda?
Greg,
@Greg - To nadal prawda. Skrypty muszą mieć możliwość zapytania o atrybuty DOM .style, aby arkusze stylów nadal blokowały wykonywanie skryptu. Oni może nie blokować skryptu załadunku , jeśli są inteligentne, ale będą blokować zdarzenia script.onLoad.
Jimmy Breck-McKye
10

Zaktualizowano 16.12.2017

Nie byłem pewien testów w OP. Postanowiłem trochę poeksperymentować i ostatecznie obaliłem niektóre z mitów.

Synchroniczny <script src...>zablokuje pobieranie zasobów poniżej, dopóki nie zostanie pobrany i wykonany

To już nie jest prawda . Spójrz na wodospad generowany przez Chrome 63:

<head>
<script src="//alias-0.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=1"></script>
<script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=2"></script>
<script src="//alias-2.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=3"></script>
</head>

Inspektor sieci Chrome -> wodospad

<link rel=stylesheet> nie blokuje pobierania i wykonywania skryptów poniżej

To jest błędne . Arkusz stylów nie będzie blokować pobieranie ale będzie blokować wykonywanie skryptów ( małe wyjaśnienie tutaj ). Spójrz na wykres wydajności wygenerowany przez Chrome 63:

<link href="//alias-0.redacted.com/payload.php?type=css&amp;delay=666" rel="stylesheet">
<script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;block=1000"></script>

Narzędzia programistyczne Chrome -> wydajność


Mając powyższe na uwadze, wyniki w OP można wyjaśnić w następujący sposób:

Najpierw CSS:

CSS Download  500ms:<------------------------------------------------>
JS Download   400ms:<-------------------------------------->
JS Execution 1000ms:                                                  <-------------------------------------------------------------------------------------------------->
DOM Ready   @1500ms:                                                                                                                                                      

JS First:

JS Download   400ms:<-------------------------------------->
CSS Download  500ms:<------------------------------------------------>
JS Execution 1000ms:                                        <-------------------------------------------------------------------------------------------------->
DOM Ready   @1400ms:                                                                                                                                            
Salman A.
źródło
Dlatego też document.write () jest jednym z najgorszych pomysłów, jakie kiedykolwiek stworzono dla HTMLDOM.
brunoais,
1
The reason is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load. javascript.info/... Dlaczego to samo założenie nie ma zastosowania w przypadku JS? Nie ma dla mnie większego sensu, kolejność wykonywanych JS nie mówi nic o jego celu.
Thorsten Schöning,
4

Nie jestem do końca pewien, w jaki sposób Twój czas „renderowania” testowania jest używany przy użyciu skryptu Java. Jednak weź to pod uwagę

Jedna strona w Twojej witrynie to 50 tys., Co nie jest nierozsądne. Użytkownik jest na wschodnim wybrzeżu, a serwer na zachodzie. MTU to zdecydowanie nie 10k, więc będzie kilka podróży tam iz powrotem. Odebranie strony i arkuszy stylów może zająć 1/2 sekundy. Zazwyczaj (dla mnie) javascript (przez wtyczkę jquery i tym podobne) to znacznie więcej niż CSS. Jest też to, co dzieje się, gdy twoje połączenie internetowe dusi się w połowie strony, ale pozwala to zignorować (zdarza mi się to czasami i wydaje mi się, że css renderuje, ale nie jestem w 100% pewien).

Ponieważ css jest na czele, mogą istnieć dodatkowe połączenia, aby go uzyskać, co oznacza, że ​​potencjalnie może zakończyć się zanim zrobi to strona. W każdym razie podczas tego typu pobierana jest pozostała część strony i pliki javascript (czyli o wiele więcej bajtów) strona jest niestylizowana, co powoduje, że strona / połączenie wydaje się być wolne.

NAWET JEŚLI interpreter JS odmawia uruchomienia, dopóki CSS nie zostanie ukończony, czas potrzebny na pobranie kodu javascript, szczególnie gdy daleko od serwera skraca się do czasu CSS, co sprawi, że strona nie będzie ładna.

Jest to niewielka optymalizacja, ale to jest tego powód.


źródło
1
serwer znajduje się na wschodnim wybrzeżu, fwiw. Najwyraźniej nie zdajesz sobie również sprawy z tego, że używają teraz sieci CDN.
jcolebrand
3

Oto PODSUMOWANIE wszystkich głównych odpowiedzi powyżej (a może poniżej później :)

W przypadku nowoczesnych przeglądarek umieść css w dowolnym miejscu. Przeanalizują twój plik HTML (który nazywają spekulatywną analizą ) i zaczną pobierać css równolegle z analizą HTML.

W przypadku starszych przeglądarek umieszczaj css na wierzchu (jeśli nie chcesz najpierw wyświetlać nagiej, ale interaktywnej strony).

W przypadku wszystkich przeglądarek umieść javascript jak najdalej na stronie, ponieważ spowoduje to zatrzymanie analizy pliku HTML. Najlepiej pobierz go asynchronicznie (tj. Wywołanie ajax)

Istnieją również pewne wyniki eksperymentalne dla konkretnego przypadku, w którym twierdzi się, że stawianie javascript na pierwszym miejscu (w przeciwieństwie do tradycyjnej mądrości stawiania CSS na pierwszym miejscu) daje lepszą wydajność, ale nie ma logicznego uzasadnienia i nie ma uzasadnienia dotyczącego szerokiego zastosowania, więc możesz zignoruj ​​to na razie.

Tak więc, aby odpowiedzieć na pytanie: Tak. Zalecenie włączenia CSS przed JS jest nieważne w nowoczesnych przeglądarkach. Umieść CSS gdziekolwiek chcesz i umieść JS pod koniec, jak to możliwe.

mehmet
źródło
1

Steve Souders udzielił już ostatecznej odpowiedzi, ale ...

Zastanawiam się, czy jest jakiś problem zarówno z oryginalnym testem Sama, jak i z powtórzeniem Josha.

Wydaje się, że oba testy zostały przeprowadzone na połączeniach o niskim opóźnieniu, w przypadku których skonfigurowanie połączenia TCP będzie trywialne.

Jak to wpływa na wynik testu Nie jestem pewien i chciałbym spojrzeć na wodospady w testach przy „normalnym” połączeniu, ale ...

Pierwszy pobrany plik powinien otrzymać połączenie użyte na stronie HTML, a drugi pobrany plik uzyska nowe połączenie. (Płukanie wczesnych zmienia tę dynamikę, ale nie robi się tutaj)

W nowszych przeglądarkach drugie połączenie TCP jest otwierane spekulacyjnie, więc obciążenie połączenia jest zmniejszone / znika, w starszych przeglądarkach nie jest to prawdą, a drugie połączenie będzie miało narzut związany z otwarciem.

Nie jestem pewien, w jaki sposób / jeśli wpływa to na wynik testów.

Andy Davies
źródło
nieprzestrzeganie, chyba że masz potokowanie, które jest niezwykle rzadkie, bardzo mało prawdopodobne jest zmniejszenie konfiguracji połączenia ... zgadzam się, że test powinien zostać powtórzony przy małym opóźnieniu
Sam Saffron
Jeśli spojrzysz na ten wodospad, zobaczysz, gdzie Chrome spekulacyjnie otwiera drugie połączenie, zanim będzie potrzebne webpagetest.org/result/… (IE9 robi to samo) ... Myślałem raczej o normalnym opóźnieniu dla celów TCP niż o niskim - jaki rodzaj środowiska, w którym przeprowadzono test?
Andy Davies
2
Re: „Steve Souders udzielił już ostatecznej odpowiedzi, ale ...” Ewolucja sieci polega na tym, że nie ma ostatecznych odpowiedzi. :) Istnieją 3-4 sposoby ładowania skryptów i rzeczy się zmieniają. Właściwie prawidłowy semantyczny powinien był powiedzieć Steve'owi „Umieść CSS przed synchronicznym JavaScript”. W przeciwnym razie ludzie popełniają błąd, generalizując, ponieważ jest to reguła dla wszystkich skryptów ...
hexalys 18.10.13
Tak, ale większość ludzi po prostu dołącza skrypty synchronicznie, więc rada Steve'a jest dobra dla niewtajemniczonych.
Andy Davies,
1

Myślę, że nie będzie to prawdą we wszystkich przypadkach. Ponieważ css będzie pobierał równolegle, ale js cant. Rozważ tę samą sprawę,

Zamiast pojedynczego css, weź 2 lub 3 pliki css i wypróbuj je w następujący sposób,

1) css..css..js 2) css..js..css 3) js..css..css

Jestem pewien, że css..css..js da lepszy wynik niż wszystkie inne.

harishkumar329
źródło
0

Musimy pamiętać, że nowe przeglądarki działały na ich silnikach Javascript, parserach itp., Optymalizując wspólne problemy z kodem i znacznikami w taki sposób, że problemy występujące w starożytnych przeglądarkach, takie jak <= IE8, nie są już istotne, nie tylko z jeśli chodzi o znaczniki, ale także użycie zmiennych JavaScript, selektorów elementów itp. W niedalekiej przyszłości widzę sytuację, w której technologia osiągnęła punkt, w którym wydajność nie jest już tak naprawdę problemem.

George Katsanos
źródło
Wydajność zawsze stanowi problem. Po prostu prawie ignoruję, że istnieją przeglądarki niezgodne ze specyfikacją. Po prostu przygotowuję mój kod tak, aby te, które podążają za specyfikacją, działały z pełną prędkością, a inne po prostu robię tak, żeby działały. Na przykład, jeśli działa tylko w IE8, wszystko w porządku.
brunoais,
-5

Osobiście nie przykładałbym zbytniej wagi do takiej „ludowej mądrości”. To, co mogło być prawdą w przeszłości, może teraz nie być prawdą. Zakładam, że wszystkie operacje związane z interpretacją i renderowaniem strony internetowej są w pełni asynchroniczne („pobieranie” czegoś i „działanie na nim” to dwie zupełnie różne rzeczy, którymi mogą zajmować się różne wątki itp. ), Oraz w każdym razie całkowicie poza twoją kontrolą lub zmartwieniem.

Umieściłem odniesienia CSS w części „head” dokumentu, wraz z wszelkimi odniesieniami do zewnętrznych skryptów. (Niektóre skrypty mogą wymagać umieszczenia w ciele, a jeśli tak, zobowiązują je.)

Poza tym ... jeśli zauważysz, że „wydaje się to szybsze / wolniejsze niż w tej / tej przeglądarce”, potraktuj to spostrzeżenie jako ciekawą, ale nieistotną ciekawość i nie pozwól, aby wpłynęło to na twoje decyzje projektowe. Zbyt wiele rzeczy zmienia się zbyt szybko. (Ktoś chce postawić zakłady na to, ile minut minie, zanim zespół Firefoksa wyjdzie z kolejnym tymczasowym wydaniem swojego produktu? Tak, ja też.)

użytkownik106701
źródło