Chrome 浏览器有意改变<link rel="stylesheet">
的加载方式,当其出如今<body>
中时,这一变化将更加明显。笔者决定在本文中进行详细说明这种改变可能带来影响与好处。css
#####一.目前CSS文件的加载方式html
<head> <link rel="stylesheet" href="/all-of-my-styles.css"> </head> <body> …content… </body>
CSS 会阻碍渲染,所以在all-of-my-styles.css
所有加载完以前,用户就只能面对一片空白的屏幕。前端
一般,咱们将某个站点的全部 CSS 样式合并为一到两个资源,这意味着用户会下载一堆当前页面根本就用不上的规则。这是由于网站可能包含许多不一样类型的页面,每一个页面都有本身的「组件」;而在组件级别传递 CSS 的话,会下降 HTTP/1 的性能。git
然而,对 SPDY 和 HTTP/2 来讲,事实却并不是如此。在这些协议中,许多小资源只须要很小的代价就能完成递送,而且被独立缓存。github
<head> <link rel="stylesheet" href="/site-header.css"> <link rel="stylesheet" href="/article.css"> <link rel="stylesheet" href="/comment.css"> <link rel="stylesheet" href="/about-me.css"> <link rel="stylesheet" href="/site-footer.css"> </head> <body> …content… </body>
这样一来就解决了冗余问题,但也意味着你须要知道输出<head>
时页面将包含的内容,从而防止 streaming。与此同时,浏览器仍是只能等待全部 CSS 样式加载完毕,才能开始渲染。若是加载 /site-footer.css
的速度不够快,就会耽误全部页面的渲染。浏览器
#####二.目前最早进的 CSS 加载方法缓存
<head> <script> // https://github.com/filamentgroup/loadCSS !function(e){"use strict" var n=function(n,t,o){function i(e){return f.body?e():void setTimeout(function(){i(e)})}var d,r,a,l,f=e.document,s=f.createElement("link"),u=o||"all" return t?d=t:(r=(f.body||f.getElementsByTagName("head")[0]).childNodes,d=r[r.length-1]),a=f.styleSheets,s.rel="stylesheet",s.href=n,s.media="only x",i(function(){d.parentNode.insertBefore(s,t?d:d.nextSibling)}),l=function(e){for(var n=s.href,t=a.length;t--;)if(a[t].href===n)return e() setTimeout(function(){l(e)})},s.addEventListener&&s.addEventListener("load",function(){this.media=u}),s.onloadcssdefined=l,l(function(){s.media!==u&&(s.media=u)}),s} "undefined"!=typeof exports?exports.loadCSS=n:e.loadCSS=n}("undefined"!=typeof global?global:this) </script> <style> /* The styles for the site header, plus: */ .main-article, .comments, .about-me, footer { display: none; } </style> <script> loadCSS("/the-rest-of-the-styles.css"); </script> </head> <body> </body>
在上面的代码中,经过一些内联样式咱们能够加速初始渲染,同时隐藏起尚未加载完样式的组件,并经过 JavaScript 异步地完成加载。剩余的 CSS 加载完后会重写.main-article
中的display:none
。性能优化
这个方法受到性能专家的推崇,他们认为这是快速完成初始渲染的好方法,而且通过实地测量确实在加载的时候快了很多。微信
但也存在一些不足之处。。。。。。app
「1.它须要一个(小的)JavaScript 库」
这是由 WebKit 的实现方式形成的。一旦页面中添加了<link rel="stylesheet">
,即便样式表是由 JavaScript 加载的,WebKit 仍是会在加载完成以前阻碍渲染。
在 Firefox 和 IE/Edge 浏览器中,经过 JS 加载样式表是彻底异步进行的。稳定版本的 Chrome 浏览器是经过 WebKit 的方式加载的,但在 Canary 版本中,仍然是使用 Firefox/Edge 加载方式。
「2.必须经历两个加载阶段」
在上述模式中,内联的 CSS 经过display:none
隐藏了没有加载完样式的内容,直到异步加载完剩余的 CSS 样式。若是你将这些样式分派到两个或多个 CSS 文件中,这些文件有可能不按照顺序加载,致使加载过程当中出现内容错乱:
内容错乱,就比如弹出广告同样,会致使用户体验挫败,必须全力消灭。
既然有两个加载阶段,你就必须决定渲染的前后顺序。你固然会想首先渲染「位置显要」的内容。可是,所谓的「位置」是根据窗口大小来决定的。所以,问题来了,你得找出一把「万能」钥匙。
#####三.一个更简单、更好的方法
<head> </head> <body> <!-- HTTP/2 push this resource, or inline it, whichever's faster --> <link rel="stylesheet" href="/site-header.css"> <header>…</header> <link rel="stylesheet" href="/article.css"> <main>…</main> <link rel="stylesheet" href="/comment.css"> <section class="comments">…</section> <link rel="stylesheet" href="/about-me.css"> <section class="about-me">…</section> <link rel="stylesheet" href="/site-footer.css"> <footer>…</footer> </body>
计划是这样的:针对每一个<link rel="stylesheet">
,加载样式表时咱们阻止渲染它的后续内容,可是容许渲染它以前的内容。样式表是并行加载的,可是按照必定的顺序显示。这使得<link rel="stylesheet">
的效用与<script src="…"></script>
相近。
假设网站 header、正文和 footer 的 CSS 已经加载完毕,但其他内容仍在等待,那么页面会是这样的:
/comment.css
)。/comment.css
)。/comment.css
)。这是一个按顺序渲染的页面。你不须要决定哪部份内容在「显要位置」,只要在页面组件第一次实例化以前引入该组件的 CSS 便可。它彻底兼容 Streaming,由于除非你须要,不然没必要要输出<link>
。
当使用内容决定布局的布局系统时(例如表格和 flexbox),要注意避免加载时出现内容错位。这不是什么新问题了,可是分步渲染会使得它出现得更为频繁。你能够经过 hack flexbox 来解决,但对总体页面布局来讲,使用 CSS grid 工具效果更佳(不过对小一些的组件来讲,flexbox 仍是很棒的)。
#####四.Chrome浏览器的改变
HTML 规范并无规定 CSS 应当怎样阻止页面渲染,它不鼓励在 body 中使用<link rel="stylesheet">
,可是全部的浏览器都容许使用。固然了,浏览器们在处理 body 中的 link 时都有本身的方法:
**Chrome和Safari:**一旦发现 <link rel="stylesheet">
就中止渲染,而且在已发现的样式表所有完成加载以前不会开始渲染。这会致使<link>
前未被渲染的内容也被阻塞。
Firefox: head中的<link rel="stylesheet">
会阻塞渲染,直至全部已发现的样式表加载完毕,body中的<link rel="stylesheet">
并不阻塞任何渲染,除非某个 head 中的样式表已经阻塞了渲染,这会致使无样式的内容出现闪烁(FOUC)。
IE/Edge: 阻塞解析器直到样式表加载完毕,可是容许渲染<link>
以前的内容。
在 Chrome 团队,咱们喜欢 IE/Edge 的方式,因此打算跟它看齐。这就容许上文描述的渐进式 CSS 渲染方式。咱们正在努力把它变成标准,从容许<body>
中的<link>
开始。
目前 Chrome/Safari 采用的方式是向下兼容的,带来的问题是阻塞渲染的时间比实际须要的长。Firefox 的方式稍微复杂一些,但有个解决的方法:
「Firefixing!」
由于 Firefox 并不老是为了<body>
中的<link>
阻塞渲染,咱们得为这个多花点功夫来避免 FOUC。谢天谢地这很容易,由于<script>
会阻塞解析,同时也会等挂起的样式表完成加载:
<link rel="stylesheet" href="/article.css"><script> </script> <main>…</main>
此处的<script>
元素必须是非空的,但加个空格足矣。
Firefox 和 Edge/IE 能够实现很美好的渐进式渲染,而 Chrome 和 Safari 在全部 CSS 加载完毕以前只能给你看一张白屏。目前 Chrome/Safari 采用的方式怎么都比将全部的样式表都放<head>
里要强,因此你如今就能够开始采用这个方法了。后面几个月,Chrome 会迁移到 Edge 的模式,这样用户就能体验更快的渲染速度了。
就是这样!经过更简单的方法加载你须要的 CSS,强力提高渲染速度。
#####五.快速定位 CSS 加载问题
那么问题来了,怎么样才能知道是否是 css 加载影响了页面的性能呢?只有定位到问题确实是 css ,老板才会给你时间和人力来优化这方面的问题对不对?
笔者以前作过前端优化的工做,国内外的前端性能优化工具也使用了很多,现阶段能够较好实现这个定位页面慢加载因素的工具备: OneAPM Browser Insight、AppDynamics、Ruxit,你们有兴趣的话能够去尝试下。
注:本文原文做者为 Jake Archibald,由 OneAPM 运营人员翻译整理
原文地址:https://jakearchibald.com/2016/link-in-body/
Browser Insight 是一个基于真实用户的 Web 前端性能监控平台,可以帮你们定位网站性能瓶颈,网站加速效果可视化;支持浏览器、微信、App 浏览 HTML 和 HTML5 页面。想阅读更多技术文章,请访问 OneAPM 官方技术博客。
本文转自 OneAPM 官方博客