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=500
daje 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&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:
Gdy najpierw dołączę kod JavaScript, wyświetlenie strony zajmuje 1,4 sekundy:
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.
delay=400&jsdelay=1000
i delay=500
który nie jest w pobliżu 100ms lub 89ms. Chyba nie jestem pewien, do jakich liczb się odnosisz.