在网上无数的地方,我都看到过在JavaScript之前使用CSS的建议。推理一般是这样的:

当涉及到CSS和JavaScript的排序时,你需要你的CSS 先来。原因是呈现线程拥有所有的 样式显示页面所需的信息。如果JavaScript include首先出现,JavaScript引擎必须先解析所有内容 继续到下一组资源。这意味着渲染 线程不能完全显示页面,因为它没有所有的 它需要的样式。

我的实际测试揭示了一些完全不同的东西:

我的测试装备

我使用下面的Ruby脚本为各种资源生成特定的延迟:

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=500给我一个500毫秒延迟传输CSS。

我使用下面的页面进行测试。

<!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秒来呈现:

当我首先包含JavaScript时,页面需要1.4秒来呈现:

我在Chrome、Firefox和ie浏览器上得到了类似的结果。然而,在Opera中,顺序并不重要。

似乎发生的情况是,JavaScript解释器在下载所有CSS之前拒绝启动。因此,首先使用JavaScript包含似乎更有效,因为JavaScript线程获得了更多的运行时间。

我遗漏了什么吗?把CSS include放在JavaScript include之前的建议是不正确的吗?

显然,我们可以添加async或使用setTimeout来释放呈现线程,或将JavaScript代码放在页脚中,或使用JavaScript加载器。这里的重点是关于基本JavaScript位和CSS位在头部的排序。


当前回答

更新2017-12-16

我不确定op的测试。我决定做一点实验,最终打破了一些神话。

同步<脚本src…>将阻止资源的下载 直到下载并执行为止

这已经不是事实了。看看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>

样式表>将不会阻止下载和执行 下面的脚本

这是不正确的。样式表不会阻止下载,但会阻止脚本的执行(这里有一点解释)。看看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>


综上所述,OP的结果可以解释如下:

CSS:

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

JavaScript:

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

其他回答

就我个人而言,我不会过分强调这种“民间智慧”。过去可能是正确的事情现在很可能不正确。我认为所有与网页解释和呈现相关的操作都是完全异步的(“获取”和“对其进行操作”是两件完全不同的事情,可能由不同的线程处理,等等),并且在任何情况下都完全超出你的控制或关注。

我将CSS引用放在文档的“头部”部分,以及对外部脚本的任何引用。(有些脚本可能要求放在主体中,如果是这样,就满足他们的要求。)

除此之外……如果您观察到“在这个/那个浏览器上,这个似乎比那个更快/更慢”,请将此观察视为有趣但无关紧要的好奇心,不要让它影响您的设计决策。太多的事情变化太快。(有人想打赌Firefox团队会在多长时间内发布他们产品的另一个临时版本吗?是的,我也是。)

史蒂夫·苏德斯已经给出了明确的答案,但是……

我想知道Sam的原始测试和Josh的重复测试是否都有问题。

这两个测试似乎都是在低延迟连接上执行的,其中建立TCP连接的开销很小。

我不确定这会如何影响测试结果,我想在“正常”延迟连接上查看测试的瀑布,但是……

下载的第一个文件将获得用于HTML页面的连接,下载的第二个文件将获得新的连接。(提前刷新<head>会改变动态,但这里没有这样做。)

在较新的浏览器中,第二个TCP连接是投机性地打开的,因此连接开销减少/消失了。在旧的浏览器中,这是不正确的,并且第二个连接将有被打开的开销。

我不确定这会如何/是否会影响测试结果。

我不会过分强调你们已经取得的成果。我相信这是主观的,但我有理由向你解释,在JavaScript之前放CSS更好。

在你的网站加载过程中,你会看到两种情况:

案例1:白屏→无风格网站→风格网站→互动→风格互动网站

案例2:白屏→无风格网站→互动→有风格网站→有风格和互动网站

我真的无法想象有人会选择情形二。这意味着使用慢速互联网连接的访问者将面对一个无风格的网站,允许他们使用JavaScript与之交互(因为JavaScript已经加载了)。此外,花在看一个无风格的网站上的时间将以这种方式最大化。为什么会有人想要那样?

它还可以更好地工作,正如jQuery所述:

“当使用依赖于CSS样式属性值的脚本时, 引用外部样式表或嵌入样式非常重要 元素”。

当文件以错误的顺序加载时(首先是JavaScript,然后是CSS),依赖于CSS文件中设置的属性的任何JavaScript代码(例如,div的宽度或高度)将无法正确加载。似乎在错误的加载顺序下,JavaScript“有时”知道正确的属性(也许这是由竞态条件引起的?)根据使用的浏览器,这种效果似乎更大或更小。

我不太确定你是如何使用JavaScript测试“渲染”时间的。然而,考虑到这一点:

One page on your site is 50 kB which is not unreasonable. The user is on the East Coast while your server is on the west. MTU is definitely not 10k so there will be a few trips back and forth. It may take 1/2 a second to receive your page and style sheets. Typically (for me) JavaScript (via jQuery plugin and such) are much more than CSS. There’s also what happens when your Internet connection chokes up midway on the page, but let’s ignore that (it happens to me occasionally and I believe the CSS renders, but I am not 100% sure).

由于CSS在头部,可能会有额外的连接来获取它,这意味着它可能会在页面完成之前完成。无论如何,在输入页面的其余部分和JavaScript文件(更多字节)时,页面是无样式的,这使得站点/连接看起来很慢。

即使JavaScript解释器在CSS完成之前拒绝启动,下载JavaScript代码所花费的时间,特别是在远离服务器时,也会减少CSS的时间,这将使网站看起来不漂亮。

这是一个小的优化,但这就是它的原因。

更新2017-12-16

我不确定op的测试。我决定做一点实验,最终打破了一些神话。

同步<脚本src…>将阻止资源的下载 直到下载并执行为止

这已经不是事实了。看看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>

样式表>将不会阻止下载和执行 下面的脚本

这是不正确的。样式表不会阻止下载,但会阻止脚本的执行(这里有一点解释)。看看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>


综上所述,OP的结果可以解释如下:

CSS:

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

JavaScript:

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