Sollte CSS immer vor Javascript stehen?


897

An unzähligen Stellen im Internet habe ich die Empfehlung gesehen, CSS vor JavaScript aufzunehmen. Die Argumentation hat im Allgemeinen folgende Form :

Wenn Sie CSS und JavaScript bestellen möchten, soll Ihr CSS an erster Stelle stehen. Der Grund dafür ist, dass der Rendering-Thread alle Stilinformationen enthält, die zum Rendern der Seite erforderlich sind. Wenn die JavaScript-Includes an erster Stelle stehen, muss die JavaScript-Engine alles analysieren, bevor Sie mit den nächsten Ressourcen fortfahren können. Dies bedeutet, dass der Rendering-Thread die Seite nicht vollständig anzeigen kann, da er nicht alle benötigten Stile enthält.

Meine eigentlichen Tests zeigen etwas ganz anderes:

Mein Testgeschirr

Ich verwende das folgende Ruby-Skript, um bestimmte Verzögerungen für verschiedene Ressourcen zu generieren:

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..."
}

Mit dem oben genannten Miniserver kann ich beliebige Verzögerungen für JavaScript-Dateien (sowohl Server als auch Client) und beliebige CSS-Verzögerungen festlegen. Zum Beispiel http://10.0.0.50:8081/test.css?delay=500gibt mir eine 500 ms Verzögerung beim Übertragen des CSS.

Ich benutze die folgende Seite zum Testen.

<!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>

Wenn ich das CSS zuerst einbinde, dauert das Rendern der Seite 1,5 Sekunden:

CSS zuerst

Wenn ich zuerst JavaScript einbinde, dauert das Rendern der Seite 1,4 Sekunden:

Zuerst JavaScript

Ich erhalte ähnliche Ergebnisse in Chrome, Firefox und Internet Explorer. In Opera spielt die Reihenfolge jedoch keine Rolle.

Was zu passieren scheint, ist, dass der JavaScript-Interpreter den Start verweigert, bis das gesamte CSS heruntergeladen ist. Es scheint also effizienter zu sein, JavaScript zuerst einzuschließen, da der JavaScript-Thread mehr Laufzeit erhält.

Fehlt mir etwas, ist die Empfehlung, CSS-Includes vor JavaScript-Includes zu platzieren, nicht korrekt?

Es ist klar, dass wir asynchron hinzufügen oder setTimeout verwenden können, um den Render-Thread freizugeben, den JavaScript-Code in die Fußzeile einzufügen oder einen JavaScript-Loader zu verwenden. Hier geht es um die Reihenfolge der wesentlichen JavaScript- und CSS-Bits im Kopf.


120
Ist 1511 vs 1422 ein statistisch signifikanter Unterschied? Das sind 6 Prozent. Die allgemeine Schwelle für bemerkenswerte bis durchschnittliche Leistungsunterschiede zwischen Menschen liegt bei etwa 20 Prozent.
Jeff Atwood

15
Der Punkt ist, dass die Neuordnung diese willkürliche Verzögerung eliminiert. Sie können die Verzögerung auf alles einstellen, was Sie wollen. Es ist nur eine Demo des Problems.
Sam Saffron

3
War deine Verspätung 100ms? Der Unterschied in Ihren Screenshots beträgt 89 ms. In Ihrer URL ist es delay=400&amp;jsdelay=1000und delay=500das ist nicht annähernd 100 ms oder 89 ms. Ich glaube, ich bin mir nicht sicher, auf welche Zahlen Sie sich beziehen.
Jeff Atwood

4
"Wenn das Javascript zuerst enthält, muss die Javascript-Engine alles analysieren, bevor Sie mit den nächsten Ressourcen fortfahren können. Dies bedeutet, dass der Rendering-Thread die Seite nicht vollständig anzeigen kann, da er nicht alle benötigten Stile enthält . " - Wenn sich das JS-Include im Kopf befindet, wird das JS ausgeführt, bevor die Seite gerendert wird, unabhängig davon, ob das CSS-Include vorher oder nachher war.
nnnnnn

162
Ich bin mir nicht sicher, ob Sie dies berücksichtigt haben, aber die Wahrnehmung der Ladezeit ist ebenfalls wichtig. Wenn Sie beispielsweise beim Laden des CSS zuerst nur die Farbe / Textur des Seitenhintergrunds erhalten, scheint dies schneller zu sein. Absolute Ladezeiten sind möglicherweise kein Hinweis darauf.
Rakesh Pai

Antworten:


712

Dies ist eine sehr interessante Frage. Ich habe meine CSS immer <link href="...">vor meine JS gestellt, <script src="...">weil "ich einmal gelesen habe, dass es besser ist". Also hast du recht; Es ist höchste Zeit, dass wir tatsächlich recherchieren!

Ich habe mein eigenes Testkabel in Node eingerichtet (Code unten). Grundsätzlich habe ich:

  • Stellen Sie sicher, dass kein HTTP-Caching vorhanden ist, damit der Browser bei jedem Laden einer Seite einen vollständigen Download durchführen muss.
  • Um die Realität zu simulieren, habe ich jQuery und das H5BP- CSS hinzugefügt (es gibt also eine anständige Menge an Skript / CSS zum Parsen).
  • Richten Sie zwei Seiten ein - eine mit CSS vor dem Skript, eine mit CSS nach dem Skript.
  • Es wurde aufgezeichnet, wie lange es gedauert hat, bis das externe Skript <head>ausgeführt wurde
  • Es wurde aufgezeichnet, wie lange es <body>gedauert hat, bis das Inline-Skript in ausgeführt wurde, was analog zu ist DOMReady.
  • Das Senden von CSS und / oder Skript an den Browser wurde um 500 ms verzögert.
  • Hat den Test 20 Mal in den 3 Hauptbrowsern ausgeführt.

Ergebnisse

Erstens mit einer um 500 ms verzögerten CSS-Datei:

     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

Als nächstes setze ich jQuery so, dass es anstelle des CSS um 500 ms verzögert wird:

     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

Schließlich habe ich sowohl jQuery als auch das CSS so eingestellt, dass es um 500 ms verzögert wird:

     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

Schlussfolgerungen

Zunächst ist zu beachten, dass ich davon ausgehe, dass sich Skripte in <head>Ihrem Dokument befinden (im Gegensatz zum Ende des Dokuments <body>). Es gibt verschiedene Argumente, warum Sie <head>am Ende des Dokuments möglicherweise auf Ihre Skripte verlinken , aber das liegt außerhalb des Rahmens dieser Antwort. Hier geht es ausschließlich darum, ob <script>s vor <link>s in die gehen soll <head>.

In modernen DESKTOP-Browsern scheint die Verknüpfung mit CSS zunächst nie einen Leistungsgewinn zu erzielen. Wenn Sie CSS nach dem Skript einfügen, erhalten Sie einen geringfügigen Gewinn, wenn sowohl CSS als auch das Skript verzögert werden, aber Sie erhalten große Gewinne, wenn CSS verzögert wird. (Wird durch die lastSpalten in der ersten Ergebnismenge angezeigt.)

Da die letzte scheint nicht zu schaden Leistung CSS Verknüpfung aber kann Gewinne unter bestimmten Umständen zur Verfügung stellen, sollten Sie auf externe Stylesheets verknüpfen , nachdem Sie auf externe Skripte verknüpfen nur auf Desktop - Browser , wenn die Leistung von alten Browsern kein Problem ist. Lesen Sie weiter für die mobile Situation.

Warum?

Wenn ein Browser auf ein <script>Tag stieß, das auf eine externe Ressource verweist, stoppt der Browser in der Vergangenheit das Parsen des HTML-Codes, ruft das Skript ab, führt es aus und analysiert den HTML-Code weiter. Wenn der Browser dagegen auf <link>ein externes Stylesheet stößt , wird der HTML-Code weiter analysiert, während die CSS-Datei (parallel) abgerufen wird.

Daher der häufig wiederholte Rat, Stylesheets an die erste Stelle zu setzen - sie würden zuerst heruntergeladen, und das erste herunterzuladende Skript könnte parallel geladen werden.

Moderne Browser (einschließlich aller Browser, mit denen ich oben getestet habe) haben jedoch spekulatives Parsing implementiert , bei dem der Browser im HTML-Code "nach vorne schaut" und mit dem Herunterladen von Ressourcen beginnt, bevor Skripte heruntergeladen und ausgeführt werden.

In alten Browsern ohne spekulatives Parsen wirkt sich das erstmalige Setzen von Skripten auf die Leistung aus, da sie nicht parallel heruntergeladen werden.

Browser-Unterstützung

Spekulatives Parsen wurde erstmals implementiert in: (zusammen mit dem Prozentsatz der weltweiten Benutzer von Desktop-Browsern, die diese Version oder höher ab Januar 2012 verwenden)

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

Insgesamt unterstützen rund 85% der heute verwendeten Desktop-Browser das spekulative Laden. Wenn Sie Skripte vor CSS stellen, wird die Leistung von 15% der Benutzer weltweit beeinträchtigt . YMMV basierend auf der spezifischen Zielgruppe Ihrer Website. (Und denken Sie daran, dass diese Zahl schrumpft.)

In mobilen Browsern ist es etwas schwieriger, endgültige Zahlen zu erhalten, da der mobile Browser und die Betriebssystemlandschaft heterogen sind. Da spekulatives Rendern in WebKit 525 (veröffentlicht im März 2008) implementiert wurde und nahezu jeder lohnende mobile Browser auf WebKit basiert, können wir den Schluss ziehen, dass "die meisten" mobilen Browser dies unterstützen sollten . Laut quirksmode verwenden iOS 2.2 / Android 1.0 WebKit 525. Ich habe keine Ahnung, wie Windows Phone aussieht.

Allerdings lief ich den Test auf meinem Android - 4 - Gerät, und während ich Zahlen sah ähnlich wie die Desktop - Ergebnisse, ich hakte es an den fantastischen neuen up Remote - Debugger für Android in Chrome und Registerkarte Netzwerk zeigte , dass der Browser tatsächlich Download wartet das CSS, bis die JavaScripts vollständig geladen sind - mit anderen Worten, selbst die neueste Version von WebKit für Android scheint spekulatives Parsen nicht zu unterstützen. Ich vermute, dass es aufgrund der CPU-, Speicher- und / oder Netzwerkeinschränkungen für mobile Geräte deaktiviert sein könnte.

Code

Vergib die Schlamperei - das war Q & D.

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));
});

jquery.js war jquery-1.7.1.min.js


137
Dies ist eine fantastische Antwort, danke für die Nutzung der Wissenschaft! Laut Ihrem Ergebnis "In modernen Browsern sieht es so aus, als würde das Verknüpfen mit CSS zuerst nie zu einem Leistungsgewinn führen" . Ich denke, die Antwort auf den Fragentitel lautet " Ja" . Der alte Rat von CSS ist zunächst eindeutig ungültig.
Jeff Atwood

In Bezug auf das Update von @ josh3736 über das Inverse auf Mobilgeräten ... ist dies ein typisches Beispiel dafür, dass Sie bei dieser bedeutenden Änderung nicht mit der Waffe springen sollten. Ich wäre gespannt, wie sich andere mobile Browser verhalten (Webkit, Gecko, Presto, Dreizack usw.), da die Leistung auf Mobilgeräten oft wichtiger ist.
Scunliffe

1
Sie sollten auch versuchen, dem Ausdrucken von CSS / JS etwas Langsamkeit hinzuzufügen, um die Geschwindigkeit eines langsamen Servers zu simulieren.
Kirb

1
"Zunächst ist zu beachten, dass ich davon ausgehe, dass sich Skripte in <head>Ihrem Dokument befinden (im Gegensatz zum Ende des Dokuments <body>)." Ich würde das viel früher in der Antwort hervorheben , wie oben. Das Einfügen eines scriptIn head, das sich auf eine externe Datei bezieht, ist aus fast jeder Perspektive fast nie korrekt (sicherlich keine Performance-Datei). Ich erinnere mich nicht, dass ich es jemals im wirklichen Leben tun musste. Die eine oder andere Zeile des Inline- Skripts vielleicht, aber das ist alles. Der Standard sollte ohne sehr gute gegenteilige Gründe das Ende des Körpers sein.
TJ Crowder

1
JQuery ist kein JavaScript und wird nur weiter aufgebläht. Daher sind die Ergebnisse mit jQuery nicht objektiv.
John

303

Es gibt zwei Hauptgründe, CSS vor JavaScript zu stellen.

  1. Alte Browser (Internet Explorer 6-7, Firefox 2 usw.) blockierten alle nachfolgenden Downloads, wenn sie mit dem Herunterladen eines Skripts begannen. Wenn Sie also a.jsgefolgt sind, werden b.csssie nacheinander heruntergeladen: zuerst a, dann b. Wenn Sie b.cssgefolgt sind, werden a.jssie parallel heruntergeladen, damit die Seite schneller geladen wird.

  2. Es wird nichts gerendert, bis alle Stylesheets heruntergeladen wurden - dies gilt für alle Browser. Skripte sind unterschiedlich - sie blockieren das Rendern aller DOM-Elemente, die sich unter dem Skript-Tag auf der Seite befinden. Wenn Sie Ihre Skripte in den HEAD einfügen, bedeutet dies, dass das Rendern der gesamten Seite blockiert ist, bis alle Stylesheets und alle Skripte heruntergeladen wurden. Es ist zwar sinnvoll, das gesamte Rendern für Stylesheets zu blockieren (damit Sie beim ersten Mal das richtige Styling erhalten und das Aufblitzen von nicht gestyltem FOUC-Inhalt vermeiden), es ist jedoch nicht sinnvoll, das Rendern der gesamten Seite für Skripte zu blockieren. Oft wirken sich Skripte nicht auf DOM-Elemente oder nur auf einen Teil der DOM-Elemente aus. Es ist am besten, Skripte so niedrig wie möglich auf der Seite zu laden oder sie noch besser asynchron zu laden.

Es macht Spaß, mit Cuzillion Beispiele zu erstellen . Diese Seite verfügt beispielsweise über ein Skript im HEAD, sodass die gesamte Seite leer bleibt, bis der Download abgeschlossen ist. Wenn wir das Skript jedoch an das Ende des BODY-Blocks verschieben, wird der Seitenkopf gerendert, da diese DOM-Elemente über dem SCRIPT-Tag auftreten, wie Sie auf dieser Seite sehen können .


10
Der geehrte Steve hat mich auf die Antwort geschlagen, aber ich werde einen Artikel hinzufügen, der für das, was er erwähnt, relevant ist: stevesouders.com/blog/2009/04/27/…
Juan Pablo Buritica

4
Sehen Sie, welche Browser das asyncAttribut unterstützen, das Steve hier empfiehlt, wenn er sagt "Laden Sie sie noch besser asynchron" - stackoverflow.com/questions/1834077/…
Jeff Atwood

Hey, kannst du mir sagen, warum jemand CSS-Dateien mit @importDirektiven verknüpfen würde ?
Josh Stodola

6
Was ist die Quelle für 2), und wenn es wahr ist, können Sie dann erklären, warum gelegentlich eine Seite den Inhalt vollständig lädt und das CSS ein oder zwei Sekunden später angewendet wird? (Dies ist selten auf meinen eigenen Seiten passiert, auf denen sich das CSS in den <head> -Tags befand)
Izkata

2
Also sollten wir jQuery+ jQuery UI+ $(document).ready(function () { });am Ende der Seite setzen? Wird es immer wie erwartet funktionieren?
Olivier Pons

42

Ich würde nicht zu viel Wert auf die Ergebnisse legen, die Sie erzielt haben. Ich glaube, dass dies subjektiv ist, aber ich habe einen Grund, Ihnen zu erklären, dass es besser ist, CSS vor js einzufügen.

Während des Ladens Ihrer Website gibt es zwei Szenarien, die Sie sehen würden:

FALL 1: weißer Bildschirm> nicht gestaltete Website> gestaltete Website> Interaktion> gestaltete und interaktive Website

FALL 2: weißer Bildschirm> nicht gestaltete Website> Interaktion> gestaltete Website> gestaltete und interaktive Website


Ich kann mir ehrlich gesagt nicht vorstellen, dass sich jemand für Fall 2 entscheidet dass Besucher, die langsame Internetverbindungen nutzen, mit einer nicht gestalteten Website konfrontiert werden, die es ihnen ermöglicht, mit Javascript zu interagieren (da diese bereits geladen ist). Darüber hinaus würde der Zeitaufwand für das Betrachten einer nicht gestalteten Website auf diese Weise maximiert. Warum sollte jemand das wollen?

Es funktioniert auch besser, wie jQuery angibt

"Wenn Sie Skripte verwenden, die auf dem Wert der CSS-Stileigenschaften basieren, ist es wichtig, auf externe Stylesheets zu verweisen oder Stilelemente einzubetten, bevor Sie auf die Skripte verweisen."

Wenn die Dateien in der falschen Reihenfolge geladen werden (zuerst JS, dann CSS), wird Javascript-Code, der auf in CSS-Dateien festgelegten Eigenschaften basiert (z. B. die Breite oder Höhe eines Div), nicht korrekt geladen. Es scheint, dass bei falscher Ladereihenfolge Javascript die richtigen Eigenschaften "manchmal" bekannt sind (möglicherweise wird dies durch eine Rennbedingung verursacht?). Dieser Effekt scheint je nach verwendetem Browser größer oder kleiner zu sein.


2
Wie würden Sie sicherstellen, dass das gesamte CSS geladen wurde, bevor das Javascript ausgeführt wird? Können Sie? oder sollte Ihr Javascript robust genug sein, um mit der Situation fertig zu werden, in der die Stile möglicherweise nicht unbedingt geladen werden müssen.
Jonnio

@Jonnio Wenn Ihr JS eine Abhängigkeit hat, sollten Sie diese Abhängigkeit explizit machen. Andernfalls treten immer seltene Zeitprobleme auf. ES6-Module sind ein guter Weg, um dies zu erreichen, aber es gibt auch viele Bibliotheken, die verwendet werden könnten.
kmkemp

26

Wurden Ihre Tests auf Ihrem PC oder auf einem Webserver durchgeführt? Es ist eine leere Seite oder ein komplexes Online-System mit Bildern, Datenbanken usw.? Führen Ihre Skripte eine einfache Hover-Ereignisaktion aus oder sind sie eine Kernkomponente für die Darstellung und Interaktion Ihrer Website mit dem Benutzer? Hier sind mehrere Dinge zu beachten, und die Relevanz dieser Empfehlungen wird fast immer zu Regeln, wenn Sie sich in eine hochkarätige Webentwicklung wagen.

Der Zweck der Regel "Stylesheets oben und Skripte unten platzieren" besteht darin, dass dies im Allgemeinen der beste Weg ist, um ein optimales progressives Rendering zu erzielen , das für die Benutzererfahrung von entscheidender Bedeutung ist.

Abgesehen von allem anderen: Wenn Ihr Test gültig ist und Sie wirklich Ergebnisse erzielen, die den gängigen Regeln widersprechen, wäre das eigentlich keine Überraschung. Jede Website (und alles, was erforderlich ist, um das Ganze auf dem Bildschirm eines Benutzers anzuzeigen) ist anders und das Internet entwickelt sich ständig weiter.


1
Ich weiß den Punkt zu schätzen, den Sie fett schreiben, aber das OP spricht darüber, was passiert, wenn Sie die Reihenfolge mit beiden oben und unten ändern .
nnnnnn

1
Also "unter der Annahme, dass [sein] Test gültig ist".
Skippr

21

Ich füge CSS-Dateien aus einem anderen Grund vor Javascript ein.

Wenn mein Javascript eine dynamische Größenanpassung für ein Seitenelement durchführen muss (für die Eckfälle, in denen CSS wirklich eine Hauptrolle im Hintergrund spielt), kann das Laden des CSS nach dem Russing des JS zu Race-Bedingungen führen, bei denen die Größe des Elements vor den CSS-Stilen geändert wird werden angewendet und sehen daher seltsam aus, wenn die Stile endlich aktiviert werden. Wenn ich das CSS vorher lade, kann ich garantieren, dass die Dinge in der beabsichtigten Reihenfolge ausgeführt werden und dass das endgültige Layout so ist, wie ich es haben möchte.


2
Dies wird eines Tages in einem Browser unterbrochen. Ich rate nicht.
Jcolebrand

1
jcolebrand: Ja, ich glaube, ich hatte nicht genug Kaffee getrunken, als ich das schrieb. (Im Nachhinein denke ich, dass die wichtigsten Dinge nur darin bestehen, das dynamische Laden von CSS zu vermeiden und das JS in ein domReady-Ereignis einzufügen, wenn Sie eine dynamische
Größenanpassung durchführen

Skripte sollten keine Anzeige ändern. Das ist der CSS-Job. HTML = Inhalt, CSS = Anzeigen von Inhalten, Javascript ändern Inhalte dynamisch. Außerdem sollte js erst nach (oder während) dem Auslösen von DOMContentLoaded mit einigen kleinen, aber sehr spezifischen Situationen handeln.
Brunoais

@brunoais: Einige Layouts können jedoch nur mit Javascript erstellt werden. Zum Beispiel muss alles, was dynamisch in der Größe geändert werden muss, über Javascript erstellt werden, und einige Dinge (wie eine Größe von 100% - 20px) erfordern, dass Javascript in alten Browsern portabel ausgeführt werden kann.
Hugomg

@missingno In diesen Fällen verwenden Sie trotzdem einfach das DOMContentLoaded-Ereignis. Aber ich verstehe was du meinst. (
Blöder

10

Ist die Empfehlung, CSS vor JavaScript einzuschließen, ungültig?

Nicht, wenn Sie es einfach als Empfehlung behandeln. Aber wenn Sie es als feste Regel behandeln? Ja, es ist ungültig.

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

Stylesheet lädt die Block-Skript-Ausführung. Wenn Sie also ein <script> After- <link rel="stylesheet" ...>Sheet haben, wird die Analyse nicht beendet - und DOMContentLoaded wird nicht ausgelöst -, bis das Stylesheet geladen ist.

Es scheint, dass Sie wissen müssen, worauf sich jedes Skript stützt, und sicherstellen müssen, dass die Ausführung des Skripts bis nach dem richtigen Abschlussereignis verzögert wird. Wenn sich das Skript nur auf das DOM stützt, kann es in ondomready / domcontentloaded fortgesetzt werden. Wenn es sich auf zu ladende Bilder oder anzuwendende Stylesheets stützt, muss dieser Code bis zum Onload-Ereignis zurückgestellt werden, wenn ich die obige Referenz richtig gelesen habe.

Ich denke nicht, dass eine Sockengröße für alle passt, obwohl sie so verkauft werden und ich weiß, dass eine Schuhgröße nicht für alle passt. Ich glaube nicht, dass es eine endgültige Antwort gibt, auf die zuerst geladen werden muss, Stile oder Skripte. Es ist eher eine Einzelfallentscheidung darüber, was in welcher Reihenfolge geladen werden muss und was bis später verschoben werden kann, da es sich nicht auf dem "kritischen Pfad" befindet.

Um mit dem Beobachter zu sprechen, der kommentierte, dass es besser ist, die Interaktionsfähigkeit des Benutzers zu verzögern, bis das Blatt hübsch ist. Es gibt viele von Ihnen da draußen und Sie ärgern Ihre Kollegen, die das Gegenteil fühlen. Sie kamen zu einer Site, um einen Zweck zu erfüllen, und Verzögerungen bei der Interaktion mit einer Site, während sie darauf warten, dass Dinge, die für das Laden keine Rolle spielen, sehr frustrierend sind. Ich sage nicht, dass Sie falsch liegen, nur, dass Sie sich bewusst sein sollten, dass es eine andere Fraktion gibt, die Ihre Priorität nicht teilt.

Diese Frage gilt insbesondere für alle Anzeigen, die auf Websites geschaltet werden. Ich würde es lieben, wenn Website-Autoren nur Platzhalter-Divs für den Anzeigeninhalt rendern und sicherstellen würden, dass ihre Website geladen und interaktiv ist, bevor die Anzeigen in ein Onload-Ereignis eingefügt werden. Selbst dann möchte ich, dass die Anzeigen nicht alle auf einmal, sondern seriell geladen werden, da sie sich auf meine Fähigkeit auswirken, sogar den Inhalt der Website zu scrollen, während die aufgeblähten Anzeigen geladen werden. Dies ist jedoch nur eine Sichtweise einer Person.

  • Kennen Sie Ihre Benutzer und was sie schätzen.
  • Kennen Sie Ihre Benutzer und die von ihnen verwendete Browserumgebung.
  • Wissen, was jede Datei tut und welche Voraussetzungen sie hat. Damit alles funktioniert, hat es Vorrang vor Geschwindigkeit und Schönheit.
  • Verwenden Sie bei der Entwicklung Tools, die Ihnen die Netzwerkzeit anzeigen.
  • Testen Sie in jeder Umgebung, die Ihre Benutzer verwenden. Möglicherweise muss die Ladereihenfolge basierend auf der Benutzerumgebung dynamisch (serverseitig beim Erstellen der Seite) geändert werden.
  • Ändern Sie im Zweifelsfall die Reihenfolge und messen Sie erneut.
  • Es ist möglich, dass das Mischen von Stilen und Skripten in der Ladereihenfolge optimal ist. nicht alle von einem, dann alle von anderen.
  • Experimentieren Sie nicht nur in welcher Reihenfolge, sondern wo. Kopf? In Körper? Nach dem Körper? DOM bereit / geladen? Geladen?
  • Berücksichtigen Sie gegebenenfalls asynchrone und verzögerte Optionen, um die Nettoverzögerung zu verringern, die dem Benutzer bevorsteht, bevor er mit der Seite interagieren kann. Testen Sie, ob sie helfen oder weh tun.
  • Bei der Bewertung der optimalen Ladereihenfolge sind immer Kompromisse zu berücksichtigen. Pretty vs. Responsive ist nur einer.

1
Der verlinkte Artikel behauptet nicht mehr "Stylesheet lädt Block-Skript-Ausführung". Ist das nicht mehr wahr?
Greg

@ Greg - Es ist immer noch wahr. Skripte müssen in der Lage sein, DOM-Stilattribute abzufragen, damit Stylesheets die Skriptausführung weiterhin blockieren. Sie könnten blockieren nicht das Laden von Skripten , wenn sie intelligent sind, aber sie blockieren script.onLoad-Ereignisse.
Jimmy Breck-McKye

10

Aktualisiert am 16.12.2017

Ich war mir über die Tests in OP nicht sicher. Ich beschloss, ein wenig zu experimentieren und zerstörte schließlich einige der Mythen.

Synchron <script src...>blockiert das Herunterladen der darunter liegenden Ressourcen, bis es heruntergeladen und ausgeführt wird

Das stimmt nicht mehr . Schauen Sie sich den von Chrome 63 erzeugten Wasserfall an:

<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>

Chrome Net Inspector -> Wasserfall

<link rel=stylesheet> blockiert nicht den Download und die Ausführung von Skripten darunter

Das ist falsch . Das Stylesheet blockiert nicht den Download, aber die Ausführung des Skripts ( kleine Erklärung hier ). Sehen Sie sich das von Chrome 63 erstellte Leistungsdiagramm an:

<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>

Chrome Dev Tools -> Leistung


Unter Berücksichtigung der obigen Ausführungen können die Ergebnisse in OP wie folgt erklärt werden:

CSS Zuerst:

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

JS Zuerst:

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

Das ist auch der Grund, warum document.write () eine der schlechtesten Ideen ist, die jemals für das HTMLDOM gemacht wurden.
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/… Warum gilt die gleiche Annahme nicht zuerst für JS? Das macht für mich nicht viel Sinn, die Reihenfolge der ausgeführten JS sagt nichts über den Zweck aus.
Thorsten Schöning

4

Ich bin mir nicht ganz sicher, wie Ihre Testzeit beim Rendern von Java-Skripten ist. Bedenken Sie dies jedoch

Eine Seite auf Ihrer Website ist 50.000, was nicht unangemessen ist. Der Benutzer befindet sich an der Ostküste, während sich Ihr Server im Westen befindet. MTU ist definitiv nicht 10k, also wird es ein paar Fahrten hin und her geben. Es kann eine halbe Sekunde dauern, bis Sie Ihre Seite und Stylesheets erhalten. Typischerweise (für mich) ist Javascript (über das JQuery-Plugin und dergleichen) viel mehr als CSS. Theres auch, was passiert, wenn Ihre Internetverbindung auf halbem Weg auf der Seite verstopft, aber lassen Sie uns das ignorieren (es passiert mir gelegentlich und ich glaube, das CSS rendert, aber ich bin nicht 100% sicher).

Da CSS im Kopf ist, gibt es möglicherweise zusätzliche Verbindungen, um es zu erhalten, was bedeutet, dass es möglicherweise beendet werden kann, bevor die Seite dies tut. Während des Typs nimmt der Rest der Seite und die Javascript-Dateien (die viel mehr Bytes enthalten) die Seite nicht mehr im Stil, wodurch die Site / Verbindung langsam erscheint.

AUCH WENN der JS-Interpreter sich weigert, zu starten, bis das CSS fertig ist, wird die Zeit zum Herunterladen des Javascript-Codes benötigt, insbesondere wenn weit vom Server entfernt die CSS-Zeit verkürzt wird, wodurch die Site nicht schön aussieht.

Es ist eine kleine Optimierung, aber das ist der Grund dafür.


1
Der Server befindet sich an der Ostküste, fwiw. Sie wissen anscheinend auch nicht, dass sie jetzt ein CDN verwenden.
Jcolebrand

3

Hier ist eine Zusammenfassung aller wichtigen Antworten oben (oder vielleicht später unten :)

Platzieren Sie CSS für moderne Browser an einer beliebigen Stelle. Sie würden Ihre HTML-Datei analysieren (was sie spekulative Analyse nennen ) und laden parallel zur HTML-Analyse CSS herunter.

Bei alten Browsern wird CSS immer weiter oben angezeigt (wenn Sie nicht zuerst eine nackte, aber interaktive Seite anzeigen möchten).

Platzieren Sie Javascript für alle Browser so weit unten wie möglich auf der Seite, da dadurch das Parsen Ihres HTML-Codes gestoppt wird. Laden Sie es vorzugsweise asynchron herunter (dh Ajax-Aufruf).

Es gibt auch einige experimentelle Ergebnisse für einen bestimmten Fall, bei denen behauptet wird, dass Javascript an erster Stelle steht (im Gegensatz zu der traditionellen Weisheit, CSS an die erste Stelle zu setzen), eine bessere Leistung liefert, aber es gibt keine logischen Gründe dafür und es fehlt eine Validierung hinsichtlich der weit verbreiteten Anwendbarkeit ignoriere es jetzt.

Um die Frage zu beantworten: Ja. Die Empfehlung, das CSS vor JS einzuschließen, ist für die modernen Browser ungültig. Platzieren Sie CSS, wo immer Sie möchten, und setzen Sie JS so weit wie möglich gegen Ende.


1

Steve Souders hat bereits eine endgültige Antwort gegeben, aber ...

Ich frage mich, ob es ein Problem mit Sams ursprünglichem Test und Joshs Wiederholung gibt.

Beide Tests wurden anscheinend für Verbindungen mit geringer Latenz durchgeführt, bei denen das Einrichten der TCP-Verbindung nur geringe Kosten verursacht.

Wie sich dies auf das Testergebnis auswirkt, weiß ich nicht genau und ich möchte mir die Wasserfälle für die Tests über eine "normale" Latenzverbindung ansehen, aber ...

Die erste heruntergeladene Datei sollte die für die HTML-Seite verwendete Verbindung erhalten, und die zweite heruntergeladene Datei erhält die neue Verbindung. (Flushing the Early verändert diese Dynamik, aber hier wird es nicht gemacht)

In neueren Browsern wird die zweite TCP-Verbindung spekulativ geöffnet, sodass der Verbindungsaufwand verringert wird / verschwindet. In älteren Browsern ist dies nicht der Fall und die zweite Verbindung hat den Aufwand, geöffnet zu werden.

Wie / ob sich dies auf das Ergebnis der Tests auswirkt, weiß ich nicht genau.


Nicht folgen, es sei denn, Sie haben Pipelining, was unglaublich selten ist. Es ist sehr unwahrscheinlich, dass der Verbindungsaufbau reduziert wird. Stimmen Sie zu, dass der Test bei geringer Latenz wiederholt werden sollte
Sam Saffron

Wenn Sie sich diesen Wasserfall ansehen, können Sie sehen, wo Chrome spekulativ eine zweite Verbindung öffnet, bevor sie benötigt wird. Webpagetest.org/result/… (IE9 macht dasselbe) ... Ich dachte eher an eine normale Latenz für TCP-Zwecke als an eine niedrige - welche Art der Umgebung wurde der Test durchgeführt?
Andy Davies

2
Betreff: "Steve Souders hat bereits eine endgültige Antwort gegeben, aber ..." Die Sache mit der Webentwicklung ist, dass es keine endgültigen Antworten gibt. :) Es gibt 3-4 Möglichkeiten, Skripte zu laden und Dinge zu ändern. Die eigentlich richtige Semantik hätte eigentlich sein müssen, dass Steve sagte "Setze CSS vor synchrones JavaScript". Andernfalls
verstehen die

Ja, aber die meisten Leute fügen Skripte nur synchron ein, sodass Steves Rat den Uneingeweihten gut tut.
Andy Davies

1

Ich denke, das wird nicht in allen Fällen zutreffen. Weil CSS parallel heruntergeladen wird, aber js kann nicht. Betrachten Sie für den gleichen Fall,

Anstatt ein einzelnes CSS zu haben, nehmen Sie 2 oder 3 CSS-Dateien und probieren Sie es auf diese Weise aus.

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

Ich bin sicher, dass css..css..js ein besseres Ergebnis liefern wird als alle anderen.


0

Wir müssen bedenken, dass neue Browser an ihren Javascript-Engines, ihren Parsern usw. gearbeitet haben und allgemeine Code- und Markup-Probleme so optimiert haben, dass Probleme, die in alten Browsern wie <= IE8 auftreten, nicht mehr relevant sind, nicht nur mit In Bezug auf Markup, aber auch auf die Verwendung von JavaScript-Variablen, Element-Selektoren usw. kann ich in nicht allzu ferner Zukunft eine Situation sehen, in der die Technologie einen Punkt erreicht hat, an dem die Leistung nicht mehr wirklich ein Problem darstellt.


Leistung ist immer ein Thema. Ich ignoriere fast, dass es Browser gibt, die nicht der Spezifikation entsprechen. Ich bereite meinen Code einfach so vor, dass diejenigen, die der Spezifikation folgen, mit voller Geschwindigkeit arbeiten und die anderen, die ich mache, so, dass es funktioniert. Wenn es zum Beispiel nur in IE8 funktioniert, ist alles in Ordnung.
Brunoais

-5

Persönlich würde ich nicht zu viel Wert auf solche "Volksweisheit" legen. Was in der Vergangenheit wahr gewesen sein mag, könnte jetzt wohl nicht wahr sein. Ich würde annehmen, dass alle Operationen, die sich auf die Interpretation und das Rendern einer Webseite beziehen, vollständig asynchron sind ("etwas holen" und "darauf einwirken" sind zwei völlig verschiedene Dinge, die von verschiedenen Threads usw. behandelt werden könnten) . ) und in jedem Fall völlig außerhalb Ihrer Kontrolle oder Ihres Anliegens.

Ich würde CSS-Verweise zusammen mit Verweisen auf externe Skripte in den "Kopf" -Teil des Dokuments einfügen. (Einige Skripte müssen möglicherweise in den Text eingefügt werden, und wenn ja, müssen Sie sie verpflichten.)

Darüber hinaus ... Wenn Sie feststellen, dass "dies in diesem / jenem Browser schneller / langsamer zu sein scheint", behandeln Sie diese Beobachtung als interessante, aber irrelevante Neugier und lassen Sie sich nicht von Ihren Entwurfsentscheidungen beeinflussen. Zu viele Dinge ändern sich zu schnell. (Möchte jemand wetten, wie viele Minuten es dauern wird, bis das Firefox-Team eine weitere Zwischenveröffentlichung seines Produkts herausbringt? Ja, ich auch nicht.)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.