在网上无数的地方,我都看到过在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位在头部的排序。


当前回答

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

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

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

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

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

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

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

其他回答

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

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

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

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

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

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

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

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

出于不同的原因,我在JavaScript之前包含了CSS文件。

如果我的JavaScript代码需要做一些页面元素的动态大小(对于那些角落的情况下,CSS是真正的主要在后面),然后在JS是russing后加载CSS可能会导致竞争条件,其中元素是在CSS样式应用之前调整大小,因此,当样式最终启动时看起来很奇怪。如果我提前加载CSS,我可以保证事情按照预期的顺序运行,最终的布局是我想要的。

您的测试是在您的个人计算机上执行的,还是在web服务器上执行的?它是一个空白的页面,还是一个包含图像、数据库等的复杂在线系统?你的脚本是执行一个简单的悬停事件动作,还是你的网站呈现和与用户交互的核心组件?这里有几件事需要考虑,当你冒险进行高水平的web开发时,这些建议的相关性几乎总是成为规则。

“将样式表放在顶部,脚本放在底部”规则的目的是,在一般情况下,这是实现最佳渐进式呈现的最佳方法,这对用户体验至关重要。

撇开其他因素不谈:假设您的测试是有效的,并且您的测试结果确实与流行规则相反,这并不令人惊讶,真的。每个网站(以及让整个网站出现在用户屏幕上的所有内容)都是不同的,互联网也在不断发展。

更新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:                                                                                                                                            ◆

2020年的答案是:这可能并不重要

这里最好的答案来自2012年,所以我决定自己测试一下。在Chrome for Android上,JS和CSS资源是并行下载的,我无法检测到页面渲染速度的差异。

我在博客上写了一篇更详细的文章