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

Sam*_*ron 884 javascript css performance

在网上无数的地方,我已经看到了在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..."
}
Run Code Online (Sandbox Code Playgroud)

上面的迷你服务器允许我为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>
Run Code Online (Sandbox Code Playgroud)

当我首先包含CSS时,页面需要1.5秒才能呈现:

CSS首先

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

首先是JavaScript

我在Chrome,Firefox和Internet Explorer中获得了类似的结果.然而,在Opera中,排序无关紧要.

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

我错过了什么,建议将CSS包含在JavaScript之前包括不正确吗?

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

jos*_*736 702

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

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

  • 确保没有HTTP缓存,因此每次加载页面时浏览器都必须进行完整下载.
  • 为了模拟现实,我包含了jQuery和H5BP CSS(所以要解析相当数量的脚本/ CSS)
  • 设置两个页面 - 一个在脚本之前使用CSS,一个在脚本之后使用CSS.
  • 记录<head>执行外部脚本所花费的时间
  • 记录了内联脚本<body>执行所需的时间,类似于DOMReady.
  • 延迟将500和/或脚本发送到浏览器500ms.
  • 在3个主要浏览器中进行了20次测试.

结果

首先,CSS文件延迟500ms:

     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
Run Code Online (Sandbox Code Playgroud)

接下来,我将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
Run Code Online (Sandbox Code Playgroud)

最后,我设置两个 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
Run Code Online (Sandbox Code Playgroud)

结论

首先,重要的是要注意我在假设<head>您的文档中包含脚本(而不是文档末尾<body>)的情况下运行.关于为什么可能链接到<head>文档末尾的脚本有各种各样的争论,但这超出了本答案的范围.这严格关于<script>s 是否应该在<link>s 之前<head>.

在现代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设备上运行测试,虽然我看到类似于桌面结果的数字,但我将它连接到Chrome for Android中的神奇新远程调试器,而Network选项卡显示浏览器实际上正在等待下载CSS直到JavaScripts完全加载 - 换句话说,即使是最新版本的WebKit for Android似乎也不支持推测性解析. 我怀疑它可能因移动设备固有的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);
});
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

test.js

var jsload = Date.now();


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

jquery.js是jquery-1.7.1.min.js

  • 这是一个很棒的答案,感谢[使用科学!](http://blog.stackoverflow.com/2011/12/dont-be-afraid-to-use-the-science/)根据您的结果**"在现代浏览器中,看起来像链接到CSS首先永远不会提供性能增益"**,我认为问题标题的答案是*是*,CSS的旧建议首先显然是无效的. (137认同)

小智 301

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

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

  2. 在下载所有样式表之前不会呈现任何内容 - 在所有浏览器中都是如此.脚本不同 - 它们阻止呈现页面中脚本标记下方的所有DOM元素.如果将脚本放在HEAD中,则意味着在下载所有样式表和所有脚本之前,将阻止整个页面呈现.虽然阻止样式表的所有渲染是有意义的(因此您第一次获得正确的样式并避免无格式内容FOUC的闪存),但阻止整个页面的脚本呈现是没有意义的.脚本通常不会影响任何DOM元素或仅影响DOM元素的一部分.最好尽可能在页面中加载脚本,或者甚至更好地异步加载它们.

使用Cuzillion创建示例很有趣.例如,此页面在HEAD中有一个脚本,因此在完成下载之前整个页面都是空白的.但是,如果我们将脚本移动到BODY块的末尾,则页眉将呈现,因为这些DOM元素出现在SCRIPT标记之上,如此页面所示.

  • 尊敬的史蒂夫打败了我的答案,但我会添加一篇与他提到的相关的文章:http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/ (10认同)
  • 2)的来源是什么,如果它是真的,你能否解释为什么有时页面会完成加载内容,然后CSS会在一两秒后应用?(这很少发生在我自己的网页上,其中CSS位于<head>标签中) (6认同)
  • 看看哪些浏览器支持`async`属性,史蒂夫在这里推荐"甚至更好地异步加载它们" - http://stackoverflow.com/questions/1834077/which-browsers-support-script-async-async (4认同)
  • 因此,我们应该在页面末尾放置jQuery + jQuery UI + $(document).ready(function(){});`吗?它会“总是”按预期工作吗? (2认同)

def*_*u1t 41

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

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

案例1:白色屏幕>无框架网站>风格网站>互动>风格和互动网站

案例2:白色屏幕>无框网站>互动>风格网站>风格和互动网站


我真的无法想象有人选择案例2.这将意味着使用慢速互联网连接的访问​​者将面临一个没有风格的网站,允许他们使用Javascript与之交互(因为已经加载).此外,查看无样式网站所花费的时间将以这种方式最大化.为什么有人想要那个?

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

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

当文件以错误的顺序加载时(首先是JS,然后是CSS),任何依赖于CSS文件中设置的属性的Javascript代码(例如div的宽度或高度)都将无法正确加载.似乎使用错误的加载顺序,Javascript"有时"知道正确的属性(可能这是由竞争条件引起的?).根据使用的浏览器,此效果似乎更大或更小.

  • 在javascript执行之前,您将如何保证所有css都已加载?你能?或者你的javascript应该足够强大,以处理可能不一定加载样式的情况. (2认同)

ski*_*ppr 26

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

"将样式表放在顶部,脚本放在底部"规则的目的是,通常,它是实现最佳渐进式渲染的最佳方式,这对用户体验至关重要.

除了所有其他方面:假设你的测试是有效的,并且你真的产生了与流行规则相反的结果,那真的不足为奇.每个网站(以及使整个事物出现在用户屏幕上所需的一切)都是不同的,互联网也在不断发展.


hug*_*omg 21

我出于不同的原因在Javascript之前包含CSS文件.

如果我的Javascript需要对某些页面元素进行动态调整(对于那些CSS实际上是后面的主要角落的情况),那么在JS正在破坏之后加载CSS会导致竞争条件,其中元素在CSS样式之前调整大小应用,因此当样式最终启动时看起来很奇怪.如果我事先加载CSS,我可以保证事情以预期的顺序运行,并且最终的布局是我想要的.

  • 这将在某些浏览器上中断一天。我没猜到 (2认同)

Ted*_*hen 10

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

如果您将其视为推荐,则不会.但是,如果你认为它是一个坚硬而快速的规则?是的,它是无效的.

来自https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded

样式表加载阻止脚本执行,所以如果你有一<script><link rel="stylesheet" ...>的页面将无法完成解析-与DOMContentLoaded不会火-直到样式表被加载.

您似乎需要知道每个脚本所依赖的内容,并确保脚本的执行延迟到正确的完成事件之后.如果脚本仅依赖于DOM,它可以在ondomready/domcontentloaded中恢复,如果它依赖于要加载的图像或要应用的样式表,那么如果我正确读取上述引用,则必须将该代码推迟到onload事件.

我不认为一个袜子尺寸适合所有,即使这是他们的销售方式,我知道一个鞋子尺寸不适合所有.我认为没有一个明确的答案,首先加载,样式或脚本.更多的是逐案决定必须以什么顺序加载什么,以及什么可以推迟到后来因为不在"关键路径".

与观察者说话,评论说,最好是延迟用户交互的能力,直到表格很漂亮.那里有很多人,你惹恼你的对手感觉相反.他们来到一个网站是为了达到目的并延迟他们与网站互动的能力,而等待完成加载无关紧要的事情是非常令人沮丧的.我并不是说你错了,只是你应该知道存在另一个不同于你优先权的派系.

此问题特别适用于放置在网站上的所有广告.如果网站作者只为广告内容呈现占位符div并确保在onload事件中注入广告之前已确保他们的网站已加载和互动,我会很高兴.即使这样,我也希望看到广告是连续加载的,而不是一次性加载,因为它们会影响我在加载膨胀广告时甚至滚动网站内容的能力.但这只是一个人的观点.

  • 了解您的用户及其重视的内容.
  • 了解您的用户以及他们使用的浏览环境.
  • 了解每个文件的作用以及它的先决条件.让一切工作将优先于速度和漂亮.
  • 使用工具在开发时显示网络时间线.
  • 在用户使用的每个环境中进行测试.可能需要动态地(服务器端,在创建页面时)根据用户环境改变加载顺序.
  • 如有疑问,请更改订单并再次测量.
  • 加载顺序中的混合样式和脚本可能是最佳的; 不是全部,而是全部.
  • 不仅要测试加载文件的顺序,还要测试其中的位置.头?身体?身体之后?DOM Ready/Loaded?装?
  • 在适当时考虑异步和延迟选项,以减少用户在与页面交互之前将经历的净延迟.测试以确定他们是否有帮助或受伤.
  • 在评估最佳负载顺序时,总会考虑权衡.漂亮与响应只是一个.


Sal*_*n A 9

更新2017-12-16

我不确定OP中的测试.我决定尝试一下,最终破坏了一些神话.

同步<script 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>
Run Code Online (Sandbox Code Playgroud)

Chrome网络检查员 - >瀑布

<link rel=stylesheet> 不会阻止下面脚本的下载和执行

这是不正确的.样式表不会阻止下载,但阻止脚本的执行(这里的解释很少).看看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>
Run Code Online (Sandbox Code Playgroud)

Chrome开发工具 - >性能


牢记以上内容,OP的结果可以解释如下:

CSS优先:

CSS Download  500ms:<------------------------------------------------>
JS Download   400ms:<-------------------------------------->
JS Execution 1000ms:                                                  <-------------------------------------------------------------------------------------------------->
DOM Ready   @1500ms:                                                                                                                                                      ?
Run Code Online (Sandbox Code Playgroud)

JS第一:

JS Download   400ms:<-------------------------------------->
CSS Download  500ms:<------------------------------------------------>
JS Execution 1000ms:                                        <-------------------------------------------------------------------------------------------------->
DOM Ready   @1400ms:                                                                                                                                            ?
Run Code Online (Sandbox Code Playgroud)


Ben*_*ann 7

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

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

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


小智 5

我不太确定您使用 JavaScript 时的测试“渲染”时间是如何的。但是,请考虑一下:

\n

您网站上的一页有 50 kB,这并非不合理。用户在东海岸,而您的服务器在西海岸。MTU肯定不是10k所以会有几次来回。可能需要 1/2 秒才能收到您的页面和样式表。通常(对我来说)JavaScript(通过jQuery插件等)比 CSS 更重要。还有\xe2\x80\x99s 当你的互联网连接在页面中途阻塞时会发生什么,但是让\xe2\x80\x99s 忽略它(我偶尔会发生这种情况,我相信 CSS 会渲染,但我不是 100%当然)。

\n

由于 CSS 位于head中中,因此可能有其他连接来获取它,这意味着它可能会在页面完成之前完成。无论如何,在页面的其余部分和 JavaScript 文件(更多字节)的类型期间,页面没有样式,这使得站点/连接显得很慢。

\n

即使JavaScript 解释器在 CSS 完成之前拒绝启动,下载 JavaScript 代码所需的时间(尤其是远离服务器时)也会占用 CSS 时间,这将使网站看起来不漂亮。

\n

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

\n