Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
280 views
in Technique[技术] by (71.8m points)

javascript - 建议在JavaScript无效之前包含CSS吗?(Is the recommendation to include CSS before JavaScript invalid?)

In countless places online I have seen the recommendation to include CSS prior to JavaScript.

(在网上无数的地方,我已经看到了在JavaScript之前包含CSS的建议。)

The reasoning is generally, of this form :

(一般来说,推理的形式如下:)

When it comes to ordering your CSS and JavaScript, you want your CSS to come first.

(在订购CSS和JavaScript时,您希望首先使用CSS。)

The reason is that the rendering thread has all the style information it needs to render the page.

(原因是渲染线程具有渲染页面所需的所有样式信息。)

If the JavaScript includes come first, the JavaScript engine has to parse it all before continuing on to the next set of resources.

(如果首先包含JavaScript,则JavaScript引擎必须先解析它,然后再继续使用下一组资源。)

This means the rendering thread can't completely show the page, since it doesn't have all the styles it needs.

(这意味着渲染线程无法完全显示页面,因为它没有所需的所有样式。)

My actual testing reveals something quite different:

(我的实际测试揭示了一些截然不同)

My test harness (我的测试工具)

I use the following Ruby script to generate specific delays for various resources:

(我使用以下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..."
}

The above mini server allows me to set arbitrary delays for JavaScript files (both server and client) and arbitrary CSS delays.

(上面的迷你服务器允许我为JavaScript文件(服务器和客户端)和任意CSS延迟设置任意延迟。)

For example, http://10.0.0.50:8081/test.css?delay=500 gives me a 500 ms delay transferring the CSS.

(例如, http://10.0.0.50:8081/test.css?delay=500 delay = 500给我一个500毫秒的延迟传输CSS。)

I use the following page to test.

(我使用以下页面进行测试。)

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

When I include the CSS first, the page takes 1.5 seconds to render:

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

CSS首先

When I include the JavaScript first, the page takes 1.4 seconds to render:

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

首先是JavaScript

I get similar results in Chrome, Firefox and Internet Explorer.

(我在Chrome,Firefox和Internet Explorer中获得了类似的结果。)

In Opera however, the ordering simply does not matter.

(然而,在Opera中,排序无关紧要。)

What appears to be happening is that the JavaScript interpreter refuses to start until all the CSS is downloaded.

(似乎正在发生的事情是JavaScript解释器在下载所有CSS之前拒绝启动。)

So, it seems that having JavaScript includes first is more efficient as the JavaScript thread gets more run time.

(因此,似乎首先使用JavaScript包含更高效,因为JavaScript线程会获得更多的运行时间。)

Am I missing something, is the recommendation to place CSS includes prior to JavaScript includes not correct?

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

It is clear that we could add async or use setTimeout to free up the render thread or put the JavaScript code in the footer, or use a JavaScript loader.

(很明显,我们可以添加async或使用setTimeout来释放渲染线程或将JavaScript代码放在页脚中,或者使用JavaScript加载器。)

The point here is about ordering of essential JavaScript bits and CSS bits in the head.

(这里的要点是关于头部中基本JavaScript位和CSS位的排序。)

  ask by Sam Saffron translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This is a very interesting question.

(这是一个非常有趣的问题。)

I've always put my CSS <link href="..."> s before my JS <script src="..."> s because "I read one time that it's better."

(我总是把我的CSS <link href="...">放在我的JS <script src="..."> s之前,因为“我读过一次它更好。”)

So, you're right;

(所以,你是对的;)

it's high time we do some actual research!

(现在是我们做一些实际研究的时候了!)

I set up my own test harness in Node (code below).

(我在Node中设置了自己的测试工具(下面的代码)。)

Basically, I:

(基本上,我:)

  • Made sure there was no HTTP caching so the browser would have to do a full download each time a page is loaded.

    (确保没有HTTP缓存,因此每次加载页面时浏览器都必须进行完整下载。)

  • To simulate reality, I included jQuery and the H5BP CSS (so there's a decent amount of script/CSS to parse)

    (为了模拟现实,我包含了jQuery和H5BP CSS(所以要解析相当数量的脚本/ CSS))

  • Set up two pages - one with CSS before script, one with CSS after script.

    (设置两个页面 - 一个在脚本之前使用CSS,一个在脚本之后使用CSS。)

  • Recorded how long it took for the external script in the <head> to execute

    (记录<head>的外部脚本执行所花费的时间)

  • Recorded how long it took for the inline script in the <body> to execute, which is analogous to DOMReady .

    (记录<body>的内联脚本执行所花费的时间,类似于DOMReady 。)

  • Delayed sending CSS and/or script to the browser by 500ms.

    (延迟将500和/或脚本发送到浏览器500ms。)

  • Ran the test 20 times in the 3 major browsers.

    (在3个主要浏览器中进行了20次测试。)

Results (结果)

First, with the CSS file delayed by 500ms:

(首先,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

Next, I set jQuery to delay by 500ms instead of the CSS:

(接下来,我将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

Finally, I set both jQuery and the CSS to delay by 500ms:

(最后,我设置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

Conclusions (结论)

First, it's important to note that I'm operating under the assumption that you have scripts located in the <head> of your document (as opposed to the end of the <body> ).

(首先,重要的是要注意我在假设您的脚本位于文档的<head> (而不是<body>的末尾)的情况下运行。)

There are various arguments regarding why you might link to your scripts in the <head> versus the end of the document, but that's outside the scope of this answer.

(关于为什么你可以链接到<head>和文档末尾的脚本有各种各样的争论,但这超出了这个答案的范围。)

This is strictly about whether <script> s should go before <link> s in the <head> .

(这严格关于<script>是否应该在<head>中的<head> <link>之前。)

In modern DESKTOP browsers, it looks like linking to CSS first never provides a performance gain.

(在现代DESKTOP浏览器中,看起来像链接到CSS首先永远不会提供性能增益。)

Putting CSS after script gets you a trivial amount of gain when both CSS and script are delayed, but gives you large gains when CSS is delayed.

(当CSS和脚本被延迟时,在脚本之后放置CSS可以获得微不足道的收益,但是当CSS延迟时会给你带来很大的收益。)

(Shown by the last columns in the first set of results.)

((由第一组结果中的last列显示。))

Given that linking to CSS last does not seem to hurt performance but can provide gains under certain circumstances, you should link to external stylesheets after you link to external scripts only on desktop browsers if the performance of old browsers is not a concern.

(鉴于最后链接到CSS并不会损害性能但在某些情况下可以提供增益,如果不考虑旧浏览器的性能,则应在仅在桌面浏览器上链接到外部脚本链接到外部样式表 。)

Read on for the mobile situation.

(继续阅读移动情况。)

Why? (为什么?)

Historically, when a browser encountered a <script> tag pointing to an external resource, the browser would stop parsing the HTML, retrieve the script, execute it, then continue parsing the HTML.

(从历史上看,当浏览器遇到指向外部资源的<script>标记时,浏览器将停止解析HTML,检索脚本,执行它,然后继续解析HTML。)

In contrast, if the browser encountered a <link> for an external stylesheet, it would continue parsing the HTML while it fetched the CSS file (in parallel).

(相反,如果浏览器遇到外部样式表的<link> ,它将在获取CSS文件(并行)时继续解析HTML。)

Hence, the widely-repeated advice to put stylesheets first – they would download first, and the first script to download could be loaded in parallel.

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

However, modern browsers (including all of the browsers I tested with above) have implemented speculative parsing , where the browser "looks ahead" in the HTML and begins downloading resources before scripts download and execute.

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

In old browsers without speculative parsing, putting scripts first will affect performance since they will not download in parallel.

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

Browser Support (浏览器支持)

Speculative parsing was first implemented in: (along with the percentage of worldwide desktop browser users using this version or greater as of Jan 2012)

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

  • Chrome 1 (WebKit 525) (100%)

    (Chrome 1(WebKit 525)(100%))

  • IE 8 (75%)

    (IE 8(75%))

  • Firefox 3.5 (96%)

    (Firefox 3.5(96%))

  • Safari 4 (99%)

    (Safari 4(99%))

  • Opera 11.60 (85%)

    (Opera 11.60(85%))

In total, roughly 85% of desktop browsers in use today support speculative loading.

(总的来说,目前使用的大约85%的桌面浏览器支持投机加载。)

Putting scripts before CSS will have a performance penalty on 15% of users globally ;

(在CSS之前放置脚本将对全球 15%的用户造成性能损失;)

YMMV based on your site's specific audience.

(YMMV基于您网站的特定受众群体。)

(And remember that number is shrinking.)

((并记住这个数字在缩小。))

On mobile browsers, it's a little harder to get definitive numbers simply due to how heterogeneous the mobile browser and OS landscape is.

(在移动浏览器上,仅仅由于移动浏览器和操作系统格局异构,获得确定数字要困难得多。)

Since speculative rendering was implemented in WebKit 525 (released Mar 2008), and just about every worthwhile mobile browser is based on WebKit, we can conclude that "most" mobile browsers should support it.

(由于推测性渲染是在WebKit 525(2008年3月发布)中实现的,几乎所有有价值的移动浏览器都基于WebKit,我们可以得出结论,“大多数”移动浏览器应该支持它。)

According to quirksmode , iOS 2.2/Android 1.0 use WebKit 525. I have no idea what Windows Phone looks like.

(根据quirksmode ,iOS 2.2 / Android 1.0使用WebKit 525.我不知道Windows Phone是什么样的。)

However, I ran the test on my Android 4 device, and while I saw numbers similar to the desktop results, I hooked it up to the fantastic new remote debugger in Chrome for Android, and Network tab showed that the browser was actually waiting to download the CSS until the JavaScripts completely loaded – in other words, even the newest version of WebKit for Android does not appear to support speculative parsing.

(但是,我在我的Android 4设备上运行测试,虽然我看到类似于桌面结果的数字,但我将它连接到Chrome for Android中的神奇新远程调试器,而Network选项卡显示浏览器实际上正在等待下载CSS直到JavaScripts完全加载 - 换句话说, 即使是最新版本的WebKit for Android似乎也不支持推测性解析。)

I suspect it might be turned off due to the CPU, memory, and/or network constraints inherent to mobile devices.

(我怀疑它可能因移动设备固有的CPU,内存和/或网络限制而被关闭。)

Code (码)

Forgive the sloppiness – this was Q&D.

(原谅这种邋 - - 这就是Q&D。)

app.js

(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(funct

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...