岁月如歌

用开放的心态,打造专业的人生。

JS 和 CSS 的位置对其他资源加载顺序的影响

with 15 comments

克军做了一系列测试:js和css的顺序关系,给出了现象和结论,但未给出原因。

JS 和 CSS 在页面中的位置,会影响其他资源(指 img 等非 js 和 css 资源)的加载顺序,究其原因,有三个值得注意的点:

  1. JS 有可能会修改 DOM. 典型的,可能会有 document.write. 这意味着,在当前 JS 加载和执行完成前,后续所有资源的下载有可能是没必要的。这是 JS 阻塞后续资源下载的根本原因。
  2. JS 的执行有可能依赖最新样式。比如,可能会有 var width = $('#id').width(). 这意味着,JS 代码在执行前,浏览器必须保证在此 JS 之前的所有 css(无论外链还是内嵌)都已下载和解析完成。这是 CSS 阻塞后续 JS 执行的根本原因。
  3. 现代浏览器很聪明,会进行 prefetch 优化。性能是如此重要,现代浏览器在竞争中,在 UI update 线程之外,还会开启另一个线程,对后续 JS 和 CSS 提前下载(注意,仅提前下载,并不执行)。有了 prefetch 优化,这意味着,在不存在任何阻塞的情况下,理论上 JS 和 CSS 的下载时机都非常优先,和位置无关。

以上三点可简述为三条基本定律:

  • 定律一:资源是否下载依赖 JS 执行结果。
  • 定律二:JS 执行依赖 CSS 最新渲染。
  • 定律三:现代浏览器存在 prefetch 优化。

有了这三条定律,再来看克军的测试,就很清晰了:

a,b – head里出现外联js,无论如何放,css文件都不能和body里的请求并行

根据定律一和定律三,可以知道上面的结论不够正确。比如:

<head>
<link rel="stylesheet" href="mock.php?css1&sleep=2">
<script src="mock.php?js&sleep=3"></script>
<link rel="stylesheet" href="mock.php?css2&sleep=4">
</head>
<body>
<link rel="stylesheet" href="mock.php?css3&sleep=5">
<img src="test.gif">
</body>

在 Chrome 下的瀑布图是:

黄色条是 js 的,可以看出 img 的延时下载是由定律一决定的。

定律三则决定了所有 js/css 都是并行开始下载的。在 Firefox 10 下,prefetch 非常强悍,对 img 也会预加载,瀑布图如下:

调整一下 sleep 时间,还可以观察到定律二的威力:

<head>
<link rel="stylesheet" href="mock.php?css1&sleep=3"> <!-- 修改 sleep 值,使其大于 js 的 -->
<script src="mock.php?js&sleep=2"></script>
<link rel="stylesheet" href="mock.php?css2&sleep=4">
</head>
<body>
<link rel="stylesheet" href="mock.php?css3&sleep=5">
<img src="test.gif">
</body>

瀑布图立刻发生了变化:

因为定律一,决定 img 的下载在 js 执行后。又因为定律二,决定 js 的执行在第一个 css 后。于是最后在瀑布图上体现出来,就是 img 的下载在第一个 css 后。

再来看克军的第二个结论:

c – head里的内联js只要在所有外联css前面,css文件可以和body里的请求并行(图2)
d – head里的内联js只要在任一外联css后面,css文件就不能和body里的请求并行(图1)

这个是定律二的威力。结论 c 是正确的,因为没有 css 会影响 js 的执行。结论 d 则不够正确。img 等其他资源,会在 js 前面的 css 下载完成后,以及 js 执行后,立刻开始下载。与头部中,js 位置之后的 css 没关系。

克军的其他结论都是对的,不多说。

注意1:Firefox 10 的 prefetch 有点奇怪,有时会对 img 进行 prefetch,有时则不会。有兴趣的可以进一步寻找规律。
注意2:上面的三个定律,是黑盒猜测,有兴趣的可以去阅读浏览器的源码,应该能找到更深层次的原因。
注意3:本文没有考虑 defer, async 属性的影响,这是另一个故事。

浏览器在迅速发展,很多总结,特别是书籍上的,很难与时俱进。大家应该像克军学习,多测试,多发现,这样得来的知识,才不会过时。这篇博客的总结,也肯定在未来甚至就在现在,已经存在错误。这些都无所谓,关键是要懂得测试的方法和分析的思路,有了“渔”,才能更好地探求和拥有“鱼”

Advertisements

Written by lifesinger

February 3, 2012 at 11:34

Posted in Articles

15 Responses

Subscribe to comments with RSS.

  1. Cool~如果这样下去,会省很多事,浏览器已经帮我们做了很多性能上的优化。

    YSjia

    February 3, 2012 at 11:49

  2. […] 感谢玉伯的这篇很好做了解释也指出一些问题: https://lifesinger.wordpress.com/2012/02/03/performance-impact-of-js-css-loading-order/ […]

  3. 印象中,chrome8左右版本有个改进是
    head里的的资源js,css加载完成前,不会有预取的做法. Firefox则没这个策略.
    Firefox4+开始对预取的策略有些调整, 调整在于 head中 css 与 js的位置. css在外链script后面时,可能会在script后面的css加载好前,提前进行首次渲染.然后等后面的css加载好后,再次更新render Tree ,并进行渲染,造成页面闪烁现象. 各种优化策略,似乎浏览器版本不同,头可能发生细微差异…

    预取的策略,似乎也在逐渐改进.最初,印象中部分浏览器的预取,只针对script ,后来开始针对其他资源,比如图片等.也会预取.

    曾经试图找出所有规律,后来思考即使整理出列表. 1不能适应所有浏览器, 更不能适应所有版本. 2 部署资源时考虑过多.无可实施性.

    最终选择的做法是. 还是基于最初的简单策略, script 越靠后越好.(至少都扔到body里) style相关尽量都扔到head里. 这也变相实现了避免 script ,css 交替防止..

    另外,觉得克军的测试,不全面. 应考虑chunked 输出,以及复杂DOM结构,复杂资源加载,网络传输速度等多方面因素..因为实测发现,这些都可能影响浏览器的策略.

    Franky

    February 3, 2012 at 14:12

  4. 不过看了玉伯的图,发现 head 和 body资源的策略似乎并不是我认为的那样, 一会去找个早期的chrome 测试看看… 不确定是改进,还是一直如此. 当初的测试看来有待商榷

    Franky

    February 3, 2012 at 14:25

  5. 得此“渔”,获益良多…

    cssbox

    February 7, 2012 at 11:30

  6. 那为什么在测试中..第一个css下载完成了js就开始执行呢? 为什么不是等三个css都下载完成再执行?

    ruohanc

    February 12, 2012 at 19:06

    • 只有 js 前面的 css 会影响 js 的执行呀。最新样式不是指所有样式,而是指当前 js 执行时,在该 js 位置之前的 css 应该都已下载并渲染好。

      lifesinger

      February 13, 2012 at 07:58

  7. […] 感谢玉伯的这篇很好做了解释也指出一些问题:https://lifesinger.wordpress.com/2012/02/03/performance-impact-of-js-css-loading-order/ This entry was posted in javascript and tagged css, js, 顺序. […]

  8. […] JS 和 CSS 的位置对其他资源加载顺序的影响 […]

  9. 在页面最前面的script似乎没有办法修改或者删除后面的资源,即使使用document.write。

    differui

    April 12, 2013 at 12:15

  10. […] 感谢玉伯的这篇很好做了解释也指出一些问题: https://lifesinger.wordpress.com/2012/02/03/performance-impact-of-js-css-loading-order/ […]

  11. Reblogged this on 我的博客.

    jiaxiong

    February 9, 2014 at 09:19

  12. 懂了,赞一个

    zheng liu

    May 21, 2014 at 18:33

  13. […] 转自 岁月如歌 […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s