968 Stimmen

Sollte CSS immer vor JavaScript stehen?

An unzähligen Stellen im Internet habe ich die Empfehlung gesehen, CSS vor JavaScript einzubinden. Die Argumentation ist im Allgemeinen, in dieser Form :

Wenn es darum geht, Ihr CSS und JavaScript zu ordnen, sollten Sie Ihr CSS an erster Stelle stehen. Der Grund dafür ist, dass der Rendering-Thread über alle Stilinformationen hat, die er zum Rendern der Seite benötigt. Wenn die JavaScript zuerst kommt, muss die JavaScript-Engine alles parsen, bevor sie bevor sie mit der nächsten Gruppe von Ressourcen fortfährt. Das bedeutet, dass der Rendering Thread kann die Seite nicht vollständig anzeigen, da er nicht über alle Stile, die er benötigt.

Mein aktueller Test zeigt etwas ganz anderes:

Mein Test-Kabelbaum

Ich verwende Folgendes Rubinrot Skript, um spezifische Verzögerungen für verschiedene Ressourcen zu erzeugen:

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

Der obige Miniserver erlaubt es mir, beliebige Verzögerungen für JavaScript-Dateien (sowohl Server als auch Client) und beliebige CSS-Verzögerungen einzustellen. Zum Beispiel, http://10.0.0.50:8081/test.css?delay=500 gibt mir eine Verzögerung von 500 ms bei der Übertragung des CSS.

Ich verwende 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 einfüge, braucht die Seite 1,5 Sekunden zum Rendern:

CSS first

Wenn ich das JavaScript zuerst einfüge, braucht die Seite 1,4 Sekunden zum Rendern:

JavaScript first

Ich erhalte ähnliche Ergebnisse in Chrome, Firefox und Internet Explorer. Unter Oper Die Reihenfolge spielt jedoch keine Rolle.

Es scheint so zu sein, dass der JavaScript-Interpreter sich weigert zu starten, bis alle CSS heruntergeladen sind. Es scheint also, dass es effizienter ist, JavaScript zuerst einzubinden, da der JavaScript-Thread mehr Laufzeit erhält.

Habe ich etwas verpasst? Ist die Empfehlung, CSS-Includes vor JavaScript-Includes zu platzieren, nicht korrekt?

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

749voto

josh3736 Punkte 130889

Dies ist eine sehr interessante Frage. Ich habe immer meine CSS <link href="..."> s vor meinem JavaScript <script src="..."> weil "ich einmal gelesen habe, dass es besser ist". Sie haben also Recht; es ist höchste Zeit, dass wir etwas recherchieren!

Ich habe mein eigenes Test-Harness in Node.js (Code unten). Im Grunde genommen, I:

  • Es wurde sichergestellt, dass es kein HTTP-Caching gibt, so dass der Browser jedes Mal, wenn eine Seite geladen wird, einen vollständigen Download durchführen muss.
  • Um die Realität zu simulieren, habe ich jQuery und das H5BP CSS (es gibt also eine ordentliche Menge an Skript/CSS zu parsen)
  • Erstellen Sie zwei Seiten - eine mit CSS vor dem Skript, eine mit CSS nach dem Skript.
  • Aufgezeichnet, wie lange das externe Skript in der <head> zur Ausführung
  • Es wurde aufgezeichnet, wie lange es dauerte, bis das Inline-Skript in der <body> auszuführen, was analog ist zu DOMReady .
  • Verzögertes Senden von CSS und/oder Skript an den Browser um 500 ms.
  • Ich habe den Test 20 Mal mit den drei wichtigsten Browsern durchgeführt.

Ergebnisse

Zunächst wird die CSS-Datei um 500 ms verzögert (die Einheit ist Millisekunden):

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

Als Nächstes habe ich jQuery auf eine Verzögerung von 500 ms anstelle von CSS eingestellt:

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

Schließlich habe ich ambos jQuery und das CSS um 500 ms zu verzögern:

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

Schlussfolgerungen

Zunächst ist es wichtig zu wissen, dass ich davon ausgehe, dass Sie Skripte im Verzeichnis <head> Ihres Dokuments (im Gegensatz zum Ende der <body> ). Es gibt verschiedene Argumente, warum Sie auf Ihre Skripte in der <head> gegen Ende des Dokuments, aber das würde den Rahmen dieser Antwort sprengen. Hier geht es ausschließlich darum, ob <script> s sollte vorgehen <link> s im <head> .

In modernen DESKTOP-Browsern, es sieht so aus, als ob die Verknüpfung mit CSS zuerst erfolgt niemals bietet einen Leistungsgewinn. Wenn Sie CSS nach dem Skript einfügen, erhalten Sie einen geringen Gewinn, wenn sowohl CSS als auch Skript verzögert werden, aber einen großen Gewinn, wenn CSS verzögert wird. (Dies wird durch die last Spalten in der ersten Ergebnismenge).

Angesichts der Tatsache, dass die Verknüpfung mit dem letzten CSS die Leistung nicht zu beeinträchtigen scheint, aber kann unter bestimmten Umständen Gewinne zu erzielen, Sie sollten auf externe Formatvorlagen verlinken nach Sie verlinken auf externe Skripte nur auf Desktop-Browsern wenn die Leistung älterer Browser keine Rolle spielt. Lesen Sie weiter, um mehr über die mobile Situation zu erfahren.

Warum?

Wenn ein Browser früher auf eine <script> Tag, das auf eine externe Ressource verweist, würde der Browser stoppen das HTML analysieren, das Skript abrufen, es ausführen und dann mit dem Parsen des HTML fortfahren. Im Gegensatz dazu würde der Browser, wenn er auf ein <link> für eine externe Formatvorlage, würde es weiter den HTML-Code zu analysieren, während die CSS-Datei (parallel) abgerufen wird.

Daher der häufig wiederholte Ratschlag, die Stylesheets zuerst zu laden - sie würden zuerst heruntergeladen, und das erste Skript, das heruntergeladen wird, könnte parallel geladen werden.

Moderne Browser (einschließlich aller Browser, die ich oben getestet habe) haben jedoch folgende Funktionen implementiert spekulatives Parsing Der Browser schaut in der HTML-Datei nach vorne und beginnt mit dem Herunterladen von Ressourcen. avant Skripte herunterladen und ausführen.

In alten Browsern ohne spekulatives Parsing beeinträchtigt das Voranstellen von Skripten die Leistung, da sie nicht parallel heruntergeladen werden können.

Browser-Unterstützung

Spekulatives Parsing wurde erstmals in implementiert: (zusammen mit dem Prozentsatz der weltweiten Nutzer von Desktop-Browsern, die diese Version oder eine höhere Version verwenden, Stand: Januar 2012)

Insgesamt unterstützen etwa 85 % der heute verwendeten Desktop-Browser spekulatives Laden. Die Verwendung von Skripten vor CSS wird bei 15 % der Nutzer zu Leistungseinbußen führen. weltweit Je nach der spezifischen Zielgruppe Ihrer Website können Sie unterschiedliche Erfahrungen machen. (Und denken Sie daran, dass diese Zahl schrumpft.)

Bei mobilen Browsern ist es etwas schwieriger, endgültige Zahlen zu erhalten, da die Landschaft der mobilen Browser und Betriebssysteme so heterogen ist. Da das spekulative Rendering in WebKit 525 (veröffentlicht im März 2008) implementiert wurde und so gut wie jeder sinnvolle mobile Browser auf WebKit basiert, können wir davon ausgehen, dass "die meisten" mobilen Browser debe es unterstützen. Laut quirksmode iOS 2.2/Android 1.0 verwenden WebKit 525. Ich habe keine Ahnung, wie Windows Phone aussieht.

Allerdings, Ich habe den Test auf meinem Android-4-Gerät durchgeführt und ähnliche Ergebnisse wie auf dem Desktop erhalten. Fern-Debugger in Chrome für Android, und Netzwerk zeigte, dass der Browser mit dem Herunterladen des CSS wartete, bis der JavaScript-Code vollständig geladen war - mit anderen Worten, selbst die neueste Version von WebKit für Android scheint kein spekulatives Parsing zu unterstützen. Ich vermute, dass diese Funktion aufgrund der CPU-, Speicher- und/oder Netzwerkbeschränkungen mobiler Geräte deaktiviert ist.

Code

Verzeihen Sie die Schlampigkeit - das war Q&D.

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

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

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

Archivo test.js

var jsload = Date.now();

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

jQuery fue jquery-1.7.1.min.js

312voto

Steve Souders Punkte 1887

Es gibt zwei Hauptgründe, CSS vor JavaScript einzusetzen.

  1. Ältere Browser (Internet Explorer 6-7, Firefox 2 usw.) blockierten alle nachfolgenden Downloads, wenn sie mit dem Herunterladen eines Skripts begannen. Wenn Sie also a.js gefolgt von b.css werden sie der Reihe nach heruntergeladen: erst a, dann b. Wenn Sie b.css gefolgt von a.js sie werden parallel heruntergeladen, so dass die Seite schneller geladen wird.

  2. Es wird nichts gerendert, bevor nicht alle Stylesheets heruntergeladen sind - dies gilt für alle Browser. Skripte sind anders - sie blockieren das Rendering aller DOM-Elemente, die unterhalb des Skript-Tags auf der Seite. Wenn Sie Ihre Skripte in den HEAD setzen, bedeutet dies, dass das Rendern der gesamten Seite blockiert ist, bis alle Stylesheets und alle Skripte heruntergeladen sind. Während es sinnvoll ist, das gesamte Rendering für Stylesheets zu blockieren (damit Sie beim ersten Mal das richtige Styling erhalten und das Aufblitzen von ungestyltem Inhalt FOUC vermeiden), ist es nicht sinnvoll, das Rendering der gesamten Seite für Skripte zu blockieren. Oft wirken sich Skripte nicht auf DOM-Elemente oder nur auf einen Teil von DOM-Elementen aus. Es ist am besten, Skripte so weit unten auf der Seite wie möglich zu laden, oder noch besser, sie asynchron zu laden.

Es macht Spaß, Beispiele zu erstellen mit Cuzillion . Zum Beispiel, diese Seite hat ein Skript im HEAD, so dass die gesamte Seite leer ist, 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 oberhalb des SCRIPT-Tags stehen, wie Sie auf diese Seite .

47voto

defau1t Punkte 10457

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

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

Fall 1 Weißer Bildschirm ungestylte Website gestylte Website Interaktion gestylte und interaktive Website

Fall 2 : weißer Bildschirm ungestylte Website Interaktion gestylte Website gestylte und interaktive Website

Ich kann mir ehrlich gesagt nicht vorstellen, dass sich jemand für Fall 2 entscheidet. Dies würde bedeuten, dass Besucher, die eine langsame Internetverbindung nutzen, mit einer ungestylten Website konfrontiert werden, die es ihnen ermöglicht, über JavaScript mit ihr zu interagieren (da dieses bereits geladen ist). Außerdem würde auf diese Weise die Zeit, die mit dem Betrachten einer ungestylten Website verbracht wird, maximiert werden. Warum sollte das jemand wollen?

Es funktioniert auch besser, da jQuery-Zustände :

"Bei der Verwendung von Skripten, die sich auf den Wert von CSS-Stileigenschaften stützen, ist es wichtig, auf externe Stylesheets zu verweisen oder Stilelemente einzubetten Elemente einbetten, bevor auf die Skripte verwiesen wird".

Wenn die Dateien in der falschen Reihenfolge geladen werden (erst JavaScript, dann CSS), wird jeder JavaScript-Code, der sich auf Eigenschaften in CSS-Dateien stützt (z. B. die Breite oder Höhe eines div ) werden nicht korrekt geladen. Es scheint, dass bei der falschen Ladereihenfolge die richtigen Eigenschaften "manchmal" JavaScript bekannt sind (vielleicht ist dies durch eine Race Condition verursacht?). Dieser Effekt scheint je nach verwendetem Browser größer oder kleiner zu sein.

29voto

skippr Punkte 2556

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

Der Zweck der Regel "Stylesheets nach oben und Skripte nach unten" ist, dass dies im Allgemeinen der beste Weg ist, um eine optimale progressives Rendering , was für das Nutzererlebnis entscheidend ist.

Abgesehen davon: Wenn Ihr Test valide ist und Sie wirklich Ergebnisse erzielen, die den gängigen Regeln widersprechen, ist das nicht weiter verwunderlich. Jede Website (und alles, was nötig ist, damit das Ganze auf dem Bildschirm eines Nutzers erscheint) ist anders, und das Internet entwickelt sich ständig weiter.

23voto

hugomg Punkte 65700

Ich schließe CSS-Dateien aus einem anderen Grund vor JavaScript ein.

Wenn mein JavaScript-Code eine dynamische Größenanpassung eines Seitenelements vornehmen muss (für diese Eckfälle, bei denen CSS wirklich eine Hauptrolle spielt), dann kann das Laden des CSS nach dem Laden des JS zu Race Conditions führen, bei denen das Element in der Größe verändert wird, bevor die CSS-Stile angewendet werden, und daher seltsam aussieht, wenn die Stile schließlich wirken. Wenn ich das CSS vorher lade, kann ich garantieren, dass die Dinge in der beabsichtigten Reihenfolge ablaufen und dass das endgültige Layout so ist, wie ich es haben möchte.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X