CSS は常に JavaScript より優先されるべきですか? 質問する

CSS は常に JavaScript より優先されるべきですか? 質問する

ネット上の数え切れないほどの場所で、JavaScriptの前にCSSを含めることを推奨しているのを見ました。その理由は一般的に、この形式の:

CSS と JavaScript の順序については、CSS を最初に配置する必要があります。これは、レンダリング スレッドに、ページをレンダリングするために必要なすべてのスタイル情報があるためです。JavaScript のインクルードが最初に配置されると、JavaScript エンジンは、次のリソース セットに進む前に、それをすべて解析する必要があります。つまり、レンダリング スレッドには必要なすべてのスタイルがないため、ページを完全に表示できません。

実際のテストでは、まったく異なる結果が出ました。

私のテストハーネス

私は以下を使用しますルビーさまざまなリソースに対して特定の遅延を生成するスクリプト:

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

上記のミニ サーバーを使用すると、JavaScript ファイル (サーバーとクライアントの両方) に任意の遅延と任意の CSS 遅延を設定できます。たとえば、http://10.0.0.50:8081/test.css?delay=500CSS の転送に 500 ミリ秒の遅延を設定します。

テストには次のページを使用します。

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

最初に CSS を含めると、ページのレンダリングに 1.5 秒かかります。

CSSをまず

最初に JavaScript を組み込むと、ページのレンダリングに 1.4 秒かかります。

まずJavaScript

Chrome、Firefox、Internet Explorerでも同様の結果が出ます。オペラただし、順序はまったく重要ではありません。

どうやら、JavaScript インタープリターは、すべての CSS がダウンロードされるまで起動を拒否しているようです。そのため、JavaScript スレッドの実行時間が長くなるため、JavaScript を最初にインクルードする方が効率的であるようです。

何か見落としているのでしょうか? CSS インクルードを JavaScript インクルードの前に配置するという推奨事項は正しくないのでしょうか?

レンダリング スレッドを解放するために async を追加したり、setTimeout を使用したり、JavaScript コードをフッターに配置したり、JavaScript ローダーを使用したりできることは明らかです。ここでのポイントは、ヘッダー内の重要な JavaScript ビットと CSS ビットの順序です。

ベストアンサー1

これは非常に興味深い質問です。私はいつも CSS<link href="...">を JavaScript より前に置きます<script src="...">。「CSS のほうが優れていると読んだことがあるから」です。ですから、おっしゃる通りです。実際に調査する時期が来ています。

私は独自のテストハーネスをセットアップしましたNode.js(コードは下記)。基本的には、次の操作を行います。

  • HTTP キャッシュが存在しないことを確認したので、ページが読み込まれるたびにブラウザは完全なダウンロードを実行する必要があります。
  • 現実をシミュレートするために、jQueryとH5BPCSS (解析するスクリプト/CSS がかなりあります)
  • 2 つのページを設定します。1 つはスクリプト前の CSS で、もう 1 つはスクリプト後の CSS です。
  • 外部スクリプトの<head>実行にかかった時間を記録しました
  • <body>内のインライン スクリプトの実行にかかった時間を記録しました。これは に類似していますDOMReady
  • CSS および/またはスクリプトのブラウザへの送信が 500 ミリ秒遅延されます。
  • 3 つの主要ブラウザでテストを 20 回実行しました。

結果

まず、CSS ファイルを 500 ミリ秒 (単位はミリ秒) 遅延させます。

     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

次に、CSS の代わりに jQuery を 500 ミリ秒遅延するように設定します。

     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

最後に、 jQuery と CSS の両方を 500 ミリ秒遅延するように設定します。

     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

結論

<head>まず、スクリプトがドキュメントの ( の末尾ではなく)にあるという前提で作業していることに注意することが重要です<body>。 でスクリプトへのリンクをとったり、ドキュメントの末尾でリンクをとったりする理由についてはさまざまな議論がありますが、それはこの回答の範囲外です。これは厳密には、内で が の前に来るべき<head>かどうかに関するものです。<script><link><head>

最新のデスクトップ ブラウザでは、最初に CSS にリンクしてもパフォーマンスが向上することはないようです。スクリプトの後に CSS を配置すると、CSS とスクリプトの両方が遅延している場合はわずかな向上が得られますが、CSS が遅延している場合は大きな向上が得られます。(last最初の結果セットの列に示されています。)

最後に CSS にリンクしてもパフォーマンスは低下せず、特定の状況下ではメリットが得られる可能性があるため、古いブラウザのパフォーマンスが問題にならない場合は、デスクトップ ブラウザでのみ、外部スクリプトにリンクした後に外部スタイル シートにリンクする必要があります。モバイルの状況については、以下をお読みください。

なぜ?

これまで、ブラウザは<script>外部リソースを指すタグに遭遇すると、 HTML の解析を停止し、スクリプトを取得して実行し、その後 HTML の解析を続行していました。対照的に、ブラウザが<link>外部スタイルシートの に遭遇すると、 CSS ファイルを(並行して)取得しながら HTML の解析を続行していました。

したがって、スタイルシートを最初に配置するというアドバイスが広く繰り返されています。スタイルシートが最初にダウンロードされ、ダウンロードされる最初のスクリプトが並行して読み込まれる可能性があります。

しかし、最近のブラウザ(私が上でテストしたすべてのブラウザを含む)では、推測的解析ブラウザは HTML を「先読み」し、スクリプトがダウンロードされて実行される前にリソースのダウンロードを開始します。

推測解析のない古いブラウザでは、スクリプトを最初に配置すると、並列ダウンロードが行われないため、パフォーマンスに影響します。

ブラウザのサポート

推測解析は、最初に次のバージョンで実装されました: (2012 年 1 月時点でこのバージョン以上を使用している世界中のデスクトップ ブラウザー ユーザーの割合とともに)

現在使用されているデスクトップ ブラウザーの約 85% が投機的読み込みをサポートしています。スクリプトを CSS の前に置くと、世界中のユーザーの 15% のパフォーマンスが低下します。サイトの特定のユーザー層によって、結果は異なる場合があります (この数は減少していることに注意してください)。

モバイル ブラウザでは、モバイル ブラウザと OS の環境が多様であるため、明確な数値を得るのが少し難しくなります。投機的レンダリングは WebKit 525 (2008 年 3 月リリース) で実装されており、価値のあるモバイル ブラウザのほとんどが WebKit をベースにしているため、「ほとんどの」モバイル ブラウザがこれをサポートすると結論付けることができます癖モードiOS 2.2/Android 1.0 は WebKit 525 を使用します。Windows Phone がどのようなものか全くわかりません。

しかし、私はAndroid 4デバイスでテストを実行しましたが、デスクトップの結果と同様の数値が表示されましたが、素晴らしい新しいリモートデバッガーAndroid 版 Chrome の[ネットワーク]タブには、ブラウザが JavaScript コードが完全に読み込まれるまで CSS のダウンロードを待機していることが示されていました。つまり、Android 版 WebKit の最新バージョンでも、推測的解析はサポートされていないようです。モバイル デバイスに固有の CPU、メモリ、ネットワークの制約により、これがオフになっているのではないかと考えています。

コード

ずさんな点はご容赦ください。これは 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だったjQuery-1.7.1.min.js より

おすすめ記事