建议在JavaScript无效之前包含CSS吗?

在网上的无数地方,我看到了在JavaScript之前包含CSS的建议。 推理通常是这种形式:

当谈到订购你的CSS和JavaScript时,你希望你的CSS排在第一位。 原因是呈现线程具有呈现页面所需的所有样式信息。 如果JavaScript包含第一,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 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秒来渲染:

首先CSS

当我首先包含JavaScript时,该页面需要1.4秒来渲染:

首先使用JavaScript

我在Chrome,Firefox和Internet Explorer中获得了类似的结果。 然而,在Opera中,排序并不重要。

看起来正在发生的事情是JavaScript解释器拒绝启动,直到下载所有的CSS。 所以,当JavaScript线程获得更多运行时间时,似乎首先使JavaScript包含效率更高。

我是否缺少一些东西,建议放置CSS包括之前的JavaScript包含不正确的东西?

很明显,我们可以添加异步或使用setTimeout释放渲染线程或将JavaScript代码放入页脚,或者使用JavaScript加载器。 这里的重点是关于头部基本JavaScript位和CSS位的排序。


这是一个非常有趣的问题。 我总是把我的CSS <link href="..."> s放在我的JS <script src="..."> s之前,因为“我读过一次,它更好。” 所以,你是对的。 现在是我们做一些实际研究的时候了!

我在Node中设置了我自己的测试工具(代码如下)。 基本上,我:

  • 确保没有HTTP缓存,因此浏览器在每次加载页面时都必须进行完整的下载。
  • 为了模拟现实,我加入了jQuery和H5BP CSS(所以有足够的脚本/ CSS来解析)
  • 设置两个页面 - 一个是脚本前的CSS,一个是脚本后的CSS。
  • 记录执行<head>的外部脚本需要多长时间
  • 记录执行<body>的内联脚本需要多长时间,这与DOMReady类似。
  • 延迟向浏览器发送CSS和/或脚本500毫秒。
  • 在三大浏览器中进行了20次测试。
  • 结果

    首先,将CSS文件延迟500毫秒:

         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
    

    接下来,我将jQuery设置为延迟500ms而不是CSS:

         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
    

    最后,我设置jQuery和CSS的由500ms的延迟:

         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
    

    结论

    首先,需要注意的是,我假设脚本位于文档的<head>中(而不是<body>的结尾)。 关于为什么你可能链接到<head>中的脚本与文档末尾有不同的论点,但这超出了本答案的范围。 这严格意味着<script>是否应该放在<head> <link>之前。

    在现代DESKTOP浏览器中,它看起来像链接到CSS首先从未提供性能增益。 在脚本之后放置CSS可以在CSS和脚本都延迟的情况下获得微不足道的收益,但是当CSS延迟时会为您带来巨大收益。 (由第一组结果的last列显示。)

    鉴于最后一次链接到CSS似乎不会损害性能,但可以在某些情况下提供收益, 因此如果不考虑旧浏览器的性能,则应在仅在桌面浏览器上链接到外部脚本后链接到外部样式表请阅读移动情况。

    为什么?

    历史上,当浏览器遇到指向外部资源的<script>标记时,浏览器将停止解析HTML,检索脚本,执行脚本,然后继续解析HTML。 相比之下,如果浏览器遇到外部样式表的<link> ,它将在并行获取CSS文件的同时继续解析HTML。

    因此,广泛重复的建议是首先放置样式表 - 它们将首先下载,并且第一个下载的脚本可以并行加载。

    然而,现代浏览器(包括我上面测试的所有浏览器)都实现了推测性解析,浏览器在HTML中“向前看”,并在脚本下载和执行之前开始下载资源。

    在没有推测性解析的旧浏览器中,首先放置脚本会影响性能,因为它们不会并行下载。

    浏览器支持

    推测性分析首先实现在:(以及2012年1月以来使用此版本或更高版本的全球桌面浏览器用户的百分比)

  • Chrome 1(WebKit 525)(100%)
  • IE 8(75%)
  • Firefox 3.5(96%)
  • Safari 4(99%)
  • Opera 11.60(85%)
  • 总的来说,目前使用的大约85%的桌面浏览器都支持投机加载。 在CSS之前放置脚本将会对全球15%的用户造成性能损失; YMMV根据您网站的特定受众。 (并且记住这个数字正在缩小。)

    在移动浏览器上,仅仅由于移动浏览器和操作系统环境的差异性而难以获得明确的数字。 由于在WebKit 525(2008年3月发布)中实施了投机渲染,并且几乎所有有价值的移动浏览器都基于WebKit,所以我们可以得出结论“大多数”移动浏览器应该支持它。 根据quirksmode,iOS 2.2 / Android 1.0使用WebKit 525.我不知道Windows Phone的外观。

    然而,我在我的Android 4设备上运行了测试,虽然我看到的数字与桌面搜索结果类似,但我将它与Android版Chrome浏览器中出色的新远程调试程序挂钩,Network选项卡显示浏览器实际上正在等待下载CSS直到JavaScript被完全加载 - 换句话说, 即使是Android的最新版本的WebKit似乎也不支持推测性分析。 我怀疑它可能由于移动设备固有的CPU,内存和/或网络限制而被关闭。

    原谅sl - - 这是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是jquery-1.7.1.min.js


    在JavaScript之前放置CSS有两个主要原因。

  • 旧版浏览器(Internet Explorer 6-7,Firefox 2等)会在开始下载脚本时阻止所有后续下载。 所以如果你有a.js后跟b.css他们会按顺序下载:先a然后b。 如果你有b.css后跟a.js他们会并行下载,所以页面加载速度更快。

  • 在所有样式表下载完毕之前不会呈现任何内容 - 所有浏览器都是如此。 脚本是不同的 - 它们阻止渲染页面中脚本标记下的所有DOM元素。 如果将脚本放在HEAD中,则意味着整个页面都将被阻止呈现,直到下载了所有样式表和所有脚本。 虽然阻止所有的样式表渲染是非常有意义的(所以你第一次得到正确的样式并且避免未格式化内容FOUC的闪光),但是阻止为整个页面渲染脚本是没有意义的。 通常,脚本不会影响任何DOM元素或DOM元素的一部分。 最好在页面中尽可能低地加载脚本,或者更好地将脚本加载到异步。

  • 用Cuzillion创造例子很有趣。 例如,此页面在HEAD中有一个脚本,因此整个页面在完成下载之前都是空白的。 但是,如果我们将脚本移动到BODY块的末尾,则页面标题将呈现,因为这些DOM元素出现在SCRIPT标记上方,如您在此页面上所看到的那样。


    我不会过分强调你所得到的结果,我相信这是主观的,但我有理由向你解释,在js之前放置CSS更好。

    在加载您的网站期间,您会看到两种情况:

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

    案例2:白屏>无风格网站>互动>风格网站>风格互动的网站

    我真的无法想象任何人选择案例2.这意味着访问者使用慢速的互联网连接将面临一个无风格的网站,允许他们使用Javascript(因为它已经加载)与它进行交互。 此外,通过这种方式可以最大限度地缩短查看未打版网站的时间。 为什么会有人想要这样?

    它也可以像jQuery那样更好地工作

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

    当文件以错误顺序(第一个JS,然后是CSS)加载时,任何依赖CSS文件中设置的属性的Javascript代码(例如div的宽度或高度)将不会正确加载。 似乎加载顺序错误,正确的属性“有时”是Javascript已知的(也许这是由竞态条件引起的)。 取决于所使用的浏览器,此效果似乎更大或更小。

    链接地址: http://www.djcxy.com/p/7151.html

    上一篇: Is the recommendation to include CSS before JavaScript invalid?

    下一篇: Vertical alignment of elements in a div