Is the recommendation to include CSS before JavaScript invalid?
在网上的无数地方,我都看到了在JavaScript之前包含CSS的建议。一般来说,推理是这样的:
When it comes to ordering your CSS and JavaScript, you want your CSS
to come first. 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. This means the rendering
thread can't completely show the page, since it doesn't have all the
styles it needs.
我的实际测试揭示了一些非常不同的事情:
我的测试装具我使用以下Ruby脚本为各种资源生成特定的延迟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 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延迟。例如,
我使用下面的页面进行测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!DOCTYPE html> <html> <head> test <script type='text/javascript'> var startTime = new Date(); <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&jsdelay=1000"> </head> <body> <p> Elapsed time is: <script type='text/javascript'> document.write(new Date() - startTime); </p> </body> </html> |
当我首先包含CSS时,页面需要1.5秒的时间来渲染:
当我首先包含javascript时,页面需要1.4秒来呈现:
我在Chrome、Firefox和Internet Explorer中得到了类似的结果。然而,在歌剧中,点菜并不重要。
似乎发生的是javascript解释器拒绝启动,直到所有的css都被下载。因此,当javascript线程获得更多运行时间时,让javascript包含First似乎更有效。
我是否遗漏了一些内容,建议将css include放在javascript include之前是否不正确?
很明显,我们可以添加async或使用setTimeout释放渲染线程,或将javascript代码放在页脚中,或使用javascript加载程序。这里的要点是关于在头部中对基本的javascript位和css位进行排序。
这是一个非常有趣的问题。我总是把我的CSS
我在节点中设置了自己的测试线束(代码如下)。基本上,我:好的。
- 确保没有HTTP缓存,因此每次加载页面时浏览器都必须进行完全下载。
- 为了模拟现实,我加入了jquery和h5bp CSS(所以有相当数量的脚本/css需要解析)
- 设置两页-一页在脚本前使用CSS,一页在脚本后使用CSS。
- 记录EDOCX1 2执行中的外部脚本花费了多长时间
- 记录EDOCX1的3行执行中的内联脚本所花的时间,这类似于EDCOX1(4)。
- 延迟发送CSS和/或脚本到浏览器500毫秒。
- 在3个主要浏览器中运行测试20次。
结果
首先,CSS文件延迟500毫秒:好的。
1 2 3 4 5 6 7 8 9 10 | 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:好的。
1 2 3 4 5 6 7 8 9 10 | 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:好的。
1 2 3 4 5 6 7 8 9 10 | 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 |
结论
首先,需要注意的是,我的操作假设您的脚本位于文档的
在现代桌面浏览器中,似乎链接到css first从未提供性能提升。当CSS和脚本都被延迟时,将CSS放在脚本之后会获得少量的收益,但当CSS被延迟时会获得巨大的收益。(由第一组结果中的
考虑到链接到css last似乎不会影响性能,但在某些情况下可以提供收益,因此,如果不关心旧浏览器的性能,则应在仅在桌面浏览器上链接到外部脚本之后链接到外部样式表。继续阅读了解移动情况。好的。为什么?
历史上,当浏览器遇到指向外部资源的
因此,将样式表放在首位的广泛重复的建议&ndash;它们将首先下载,而要下载的第一个脚本可以并行加载。好的。
然而,现代浏览器(包括我在上面测试过的所有浏览器)已经实现了推测性的解析,即浏览器在HTML中"向前看",并在脚本下载和执行之前开始下载资源。好的。
在没有推测性解析的旧浏览器中,将脚本放在第一位会影响性能,因为它们不会并行下载。好的。浏览器支持
推测性分析最早于年实现:(截至2012年1月,使用此版本或更高版本的全球桌面浏览器用户的百分比)好的。
- Chrome 1(WebKit 525)(100%)
- IE 8(75%)
- 火狐3.5(96%)
- Safari 4(99%)
- 歌剧11.60(85%)
总的来说,目前大约85%的桌面浏览器支持推测性加载。将脚本放在CSS之前会对全球15%的用户造成性能损失;YMMV基于您的站点的特定受众。(记住这个数字正在缩小。)好的。
在移动浏览器上,仅仅因为移动浏览器和操作系统的环境是多么的异构,所以要获得明确的数字有点困难。由于推测性渲染是在WebKit525(2008年3月发布)中实现的,而且几乎每一个有价值的移动浏览器都是基于WebKit的,因此我们可以得出结论,"大多数"移动浏览器应该支持它。根据QuirksMode,iOS 2.2/android 1.0使用WebKit525。我不知道Windows Phone是什么样子。好的。
但是,我在我的Android 4设备上运行了测试,虽然我看到了与桌面结果类似的数字,但我还是将它连接到了Chrome for Android的奇妙的新远程调试器上,"网络"选项卡显示浏览器实际上正在等待下载CSS,直到Javascripts完全加载&ndash;换句话说,即使是最新版本的F Webkit for Android似乎不支持推测性分析。我怀疑它可能是由于移动设备固有的CPU、内存和/或网络限制而关闭的。好的。代码
原谅那些草率的人吧,这就是问题所在。好的。
App.JS好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 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+=' ' + i; }); o+=' '; bodyresults[req.params.type].forEach(function(i) { o+=' ' + i; }); res.send(o); }); |
CSS.HTML好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!DOCTYPE html> <html> <head> CSS first var start = Date.now(); <link rel="stylesheet" href="style.css"> <script src="jquery.js"> <script src="test.js"> </head> <body> document.write(jsload - start);bodyexec=Date.now() </body> </html> |
JS HTML好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!DOCTYPE html> <html> <head> CSS first var start = Date.now(); <script src="jquery.js"> <script src="test.js"> <link rel="stylesheet" href="style.css"> </head> <body> document.write(jsload - start);bodyexec=Date.now() </body> </html> |
JS好的。
1 2 3 4 5 6 | var jsload = Date.now(); $(function() { $.post('/result' + location.pathname.replace('.html','') + '/' + (jsload - start) + '/' + (bodyexec - start)); }); |
jquery.js是jquery-1.7.1.min.js好的。好啊。
把CSS放在JavaScript之前有两个主要原因。
旧浏览器(Internet Explorer 6-7、Firefox 2等)在开始下载脚本时会阻止所有后续下载。因此,如果你有
在下载所有样式表之前不会呈现任何内容——这在所有浏览器中都是正确的。脚本是不同的-它们会阻止页面中脚本标记下面的所有DOM元素的呈现。如果将脚本放在头部,则意味着在下载所有样式表和所有脚本之前,将阻止整个页面呈现。虽然为样式表阻止所有呈现是有意义的(这样您第一次获得正确的样式并避免未样式内容的闪烁),但是为脚本阻止整个页面的呈现是没有意义的。通常,脚本不会影响任何DOM元素或只是一部分DOM元素。最好尽可能低地加载页面中的脚本,或者更好地异步加载它们。
用Cuzillion创建示例很有趣。例如,这个页面的头部有一个脚本,所以整个页面在下载完成之前都是空白的。但是,如果我们将脚本移动到body块的末尾,则由于这些dom元素出现在脚本标记的上方,所以页面标题会呈现出来,如您在本页上看到的。
我不会太强调你得到的结果,我相信这是主观的,但我有理由向你解释,最好在JS之前输入CSS。
在加载网站的过程中,您将看到两种情况:
案例1:白屏>非样式化网站>样式化网站>交互>样式化和交互网站案例2:白屏>非样式化网站>交互>样式化网站>样式化和交互网站我真的无法想象有人会选择第二种情况。这意味着使用慢速互联网连接的访问者将面临一个非样式化的网站,允许他们使用javascript与之交互(因为已经加载了该网站)。此外,通过这种方式,可以最大限度地延长浏览非样式网站的时间。为什么会有人想要?
它在jquery状态下也工作得更好
"When using scripts that rely on the value of CSS style properties,
it's important to reference external stylesheets or embed style
elements before referencing the scripts".
当文件以错误的顺序加载时(首先是JS,然后是CSS),依赖于CSS文件中设置的属性的任何JavaScript代码(例如,DIV的宽度或高度)都将无法正确加载。在错误的加载顺序下,正确的属性对于javascript来说"有时"是已知的(也许这是由争用条件引起的?)。根据所使用的浏览器,此效果似乎更大或更小。
您的测试是在您的个人计算机上执行的,还是在Web服务器上执行的?它是一个空白页,还是一个复杂的在线系统,包含图像、数据库等?您的脚本是否执行简单的悬停事件操作,或者它们是您的网站如何呈现和与用户交互的核心组件?这里有几件事要考虑,当你冒险进入高质量的Web开发时,这些建议的相关性几乎总是成为规则。
"在顶部放置样式表和底部脚本"的目的是,一般来说,它是实现最佳渐进渲染的最佳方式,这对于用户体验至关重要。
除此之外,假设你的测试是有效的,而且你真的在产生与流行的规则相反的结果,那就不足为奇了。每一个网站(以及使整个东西出现在用户屏幕上所需的一切)都是不同的,互联网也在不断发展。
我把CSS文件放在javascript之前是因为另一个原因。
如果我的javascript需要动态调整某个页面元素的大小(对于那些在后面CSS确实是主要元素的角情况),那么在JS被腐蚀后加载CSS可能会导致竞争条件,在应用CSS样式之前调整元素的大小,从而在样式最终开始时看起来很奇怪。如果我事先加载CSS,我可以保证事情按预期的顺序运行,并且最终的布局就是我想要的。
建议在javascript无效之前包含css吗?
如果你把它当作一个简单的建议,那就不是了。但如果你把它当作一个硬性的快速规则?,是的,它是无效的。
来自https://developer.mozilla.org/en-us/docs/web/reference/events/domcontentloaded
Stylesheet loads block script execution, so if you have a
after athe page will not finish parsing
- and DOMContentLoaded will not fire - until the stylesheet is loaded.
您似乎需要知道每个脚本依赖什么,并确保脚本的执行延迟到正确的完成事件之后。如果脚本只依赖于dom,它可以在ondomready/domcontentloaded中恢复,如果它依赖于要加载的图像或要应用的样式表,那么如果我正确地读取了上述引用,那么该代码必须延迟到onload事件。
我不认为一个袜子大小适合所有人,即使这是他们的销售方式,我知道一个鞋大小不适合所有人。我不认为有一个明确的答案要先加载样式或脚本。它更多的是一个个案决定什么必须以什么顺序加载,什么可以推迟到以后作为不在"关键路径"上。
与评论说最好延迟用户交互的能力,直到工作表漂亮为止的观察者交谈。你们中的很多人都在外面,你让对方觉得你很讨厌。他们来到一个站点是为了完成一个目标,而他们在等待不重要的事情完成加载的同时,延迟了与一个站点的交互能力,这是非常令人沮丧的。我不是说你错了,只是说你应该意识到存在着另一个派别,而不是你的优先权。
这个问题特别适用于网站上的所有广告。如果网站作者只呈现广告内容的占位符,并确保他们的网站在将广告插入到OnLoad事件之前已加载并交互,我会很高兴的。即便如此,我还是希望看到连续加载的广告而不是一次加载所有的广告,因为它们会影响我在加载膨胀的广告时滚动网站内容的能力。但这只是一个人的观点。
- 了解您的用户及其价值。
- 了解您的用户及其使用的浏览环境。
- 了解每个文件的功能及其先决条件。让一切正常运转将优先于速度和漂亮。
- 开发时使用显示网络时间线的工具。
- 在用户使用的每个环境中进行测试。可能需要根据用户环境动态(服务器端,创建页面时)更改加载顺序。
- 如有疑问,请更改顺序并再次测量。
- 在加载顺序中混合样式和脚本可能是最佳的;不是所有样式和脚本都是最佳的。
- 实验不只是加载文件的顺序,而是在哪里。Head?体内?身体之后?DOM准备好了吗?加载?
- 在适当的时候考虑ASYNC和延迟选项,以减少用户在与页面交互之前体验到的网络延迟。测试以确定它们是否有帮助或伤害。
- 在评估最佳负荷顺序时,总是要考虑权衡。漂亮与反应灵敏仅为一。
更新2017至12 16
我对OP的测试不太确定。我决定试一下,最后揭穿一些神话。
Synchronous
will block downloading of the resources
below it until it is downloaded and executed
这不再是真的了。看看Chrome63生成的瀑布:
1 2 3 4 5 | <head> <script src="//alias-0.redacted.com/payload.php?type=js&delay=333&rand=1"> <script src="//alias-1.redacted.com/payload.php?type=js&delay=333&rand=2"> <script src="//alias-2.redacted.com/payload.php?type=js&delay=333&rand=3"> </head> |
will not block download and execution of
scripts below it
这是不正确的。样式表不会阻止下载,但会阻止脚本的执行(这里没有什么解释)。看看Chrome63生成的性能图表:
1 2 | <link href="//alias-0.redacted.com/payload.php?type=css&delay=666" rel="stylesheet"> <script src="//alias-1.redacted.com/payload.php?type=js&delay=333&block=1000"> |
记住上述内容,OP中的结果可以解释为:
CSS第一:
1 2 3 4 | CSS Download 500ms:<------------------------------------------------> JS Download 400ms:<--------------------------------------> JS Execution 1000ms: <--------------------------------------------------------------------------------------------------> DOM Ready @1500ms: ◆ |
JS First:
1 2 3 4 | JS Download 400ms:<--------------------------------------> CSS Download 500ms:<------------------------------------------------> JS Execution 1000ms: <--------------------------------------------------------------------------------------------------> DOM Ready @1400ms: ◆ |
我不太确定您的测试"渲染"时间是如何使用Java脚本的。但是考虑一下这个
你网站上的一页是50K,这不是不合理的。当您的服务器位于西海岸时,用户位于东海岸。MTU肯定不是10公里,所以会有一些来回旅行。接收页面和样式表可能需要1/2秒。通常(对我来说)JavaScript(通过jQuery插件等)比CSS要多得多。另外,当你的互联网连接在页面中间阻塞时,会发生什么,但是让我们忽略它(偶尔发生在我身上,我相信CSS呈现,但我不是100%肯定)。
因为CSS在头上,所以可能有额外的连接来获得它,这意味着它可以在页面完成之前完成。不管怎样,在页面剩下的类型和页面的JavaScript文件(这是多字节)的情况下,页面的连接速度会很慢。
即使JS解释器在CSS完成之前拒绝启动,下载JavaScript代码所需的时间也会减少,尤其是在远离服务器的地方,这会使站点看起来不漂亮。
这是一个小的优化,但这就是它的原因。
下面是上面所有主要答案的摘要(或者稍后可能会在下面)。
对于现代浏览器,将CSS放在您喜欢的地方。他们会分析您的HTML文件(他们称之为推测性分析),并开始下载与HTML分析并行的CSS。
对于旧的浏览器,继续将CSS放在最上面(如果你不想先显示一个裸体但交互式的页面)。
对于所有浏览器,尽可能将javascript放在页面的下方,因为它将停止解析HTML。最好是异步下载(即Ajax调用)
也有一些实验结果表明,首先提出JavaScript(与传统的将CSS放在首位的智慧相比)提供更好的性能,但是没有给出逻辑推理,并且缺乏广泛适用性的验证,所以现在可以忽略它。
所以,回答这个问题:是的。在JS之前包含CSS的建议对于现代浏览器无效。把CSS放在任何你喜欢的地方,尽可能把JS放在末尾。
我认为这对所有的案件都不是真的。因为CSS将下载并行但JS不能。考虑同样的情况,
不要使用单一的CSS,而是使用2或3个CSS文件并尝试这些方法,
1)CSS、CSS、JS2)CSS、JS、CSS3)JS…CSS…CSS
我敢肯定css..css..js会比其他所有样式的效果更好。
史蒂夫·桑德斯已经给出了一个明确的答案,但是…
我想知道山姆的原始测试和乔希的重复测试是否都有问题。
这两个测试似乎都是在低延迟连接上执行的,在低延迟连接上设置TCP连接的成本很低。
这是如何影响测试结果的,我不确定,我想通过"正常"延迟连接查看测试的瀑布,但是…
下载的第一个文件将获得用于HTML页面的连接,下载的第二个文件将获得新的连接。(冲洗早期会改变这种动态,但这里没有这样做)
在较新的浏览器中,第二个TCP连接被推测性地打开,从而减少/消除连接开销;在较旧的浏览器中,这不是真的,第二个连接将有打开的开销。
我不确定这会如何影响测试结果。
我们必须记住,新的浏览器已经在他们的javascript引擎、解析器等上进行了工作,优化了常见的代码和标记问题,这样一来,在古代浏览器中遇到的问题(如<=ie8)就不再相关,不仅与标记有关,而且与javascript变量、元素选择器等的使用也无关。我可以看到,在不久的将来,技术已经达到了一个点,性能不再是真正的问题。
就我个人而言,我不会过于强调这种"民间智慧",过去可能是真的,现在可能不是真的。我假设与网页的解释和呈现相关的所有操作都是完全异步的("获取"和"执行"是两个完全不同的东西,可能由不同的线程处理,等等),在任何情况下都完全超出了您的控制或关注范围。
我将把CSS引用和任何对外部脚本的引用放在文档的"head"部分。(有些脚本可能需要放在正文中,如果需要,请强制执行。)
除此之外…如果您观察到"在这个/那个浏览器上,这似乎比那个更快/更慢",请将这个观察视为一个有趣但不相关的好奇心,不要让它影响您的设计决策。太多的事情变化太快。(有人想赌在火狐团队推出他们产品的另一个临时版本之前还有多少分钟?是的,我也是。)