CSS 加载新方式

Chrome 浏览器有意改变<link rel="stylesheet">的加载方式,当其出现在<body>中时,这一变化将更加明显。笔者决定在本文中进行详细说明这种改变可能带来影响与好处。

一.目前CSS文件的加载方式
<head>
  <link rel="stylesheet" href="/all-of-my-styles.css">
</head>
<body>
  …content…
</body>

CSS 会阻碍渲染,因此在all-of-my-styles.css全部加载完之前,用户就只能面对一片空白的屏幕。

通常,我们将某个站点的所有 CSS 样式合并为一到两个资源,这意味着用户会下载一堆当前页面根本就用不上的规则。这是因为网站可能包含许多不同类型的页面,每个页面都有自己的「组件」;而在组件级别传递 CSS 的话,会降低 HTTP/1 的性能。

然而,对 SPDY 和 HTTP/2 来说,事实却并非如此。在这些协议中,许多小资源只需要很小的代价就能完成递送,并且被独立缓存。

<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

这个方法受到性能专家的推崇,他们认为这是快速完成初始渲染的好方法,并且经过实地测量确实在加载的时候快了不少。

但也存在一些不足之处。。。。。。

「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 已经加载完毕,但其余内容仍在等待,那么页面会是这样的:

  • Header:已渲染
  • 正文:已渲染
  • 评论部分:未渲染,它前面的 CSS 还未被加载(/comment.css)。
  • 关于本站:未渲染。它前面的 CSS 还未被加载(/comment.css)。
  • Footer:未渲染。尽管它本身的 CSS 已加载完成,但它前面的 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 InsightAppDynamicsRuxit,大家有兴趣的话可以去尝试下。

注:本文原文作者为 Jake Archibald,由 OneAPM 运营人员翻译整理

原文地址:https://jakearchibald.com/2016/link-in-body/

时间: 2024-09-20 00:03:23

CSS 加载新方式的相关文章

邮箱css加载失败怎么办

  故障分析:造成css加载失败的原因有很多,学无忧也遇到过,这可能跟你代码出错,浏览器.路径.编码等等都是有关联的.所以在具体情况具体分析.下面看看具体的佳佳方案. 一.网站代码本身问题. js或css路径.先检查一下你的语法有没有错误.三种方式的使用如果你写的代码没有错误的法应该不会加载错误.可以请专业人士帮你查看分析下代码. 二.路径问题 如果只是在本地进行代码编写和测试出现css价值失败,那么可能是你的保存路径所导致,这个相对路径和绝对路径脱离不了关系.所以检查好你的网站路径恢复即可解决

多种js图片预加载实现方式分享_javascript技巧

图片预加载有大体有几种方式  1.html标签或css加载图片 显而易见我们使用img标签或者通过标签的background-image属性都可以实现图片的预加载.但是为了避免初次载入过多图片影响体验.一般最好在文档渲染完成以后再加载(使用window.onload等).  2.纯js实现预加载 空城计-Code记的Javascript实现图片的预加载的完整实现的预加载实例为 function preloadimages(arr){ var newimages=[], loadedimages=

信息页文内画中画广告js实现代码(文中加载广告方式)_javascript技巧

小编就直接给代码了: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv=&qu

现在hao123网站数据采用异步加载的方式,有没有方法可以比较简单的抓取或解析出来

问题描述 现在hao123网站数据采用异步加载的方式,有没有方法可以比较简单的抓取或解析出来 现在hao123网站数据采用异步加载的方式,用jsoup或httpclient都无法将页面内容取出来,是我没有用来jsoup或httpclient还是解析不了或抓不了,有没有什么方法可以解析.求大神指点.

通过css加载远程字体示例代码

为了让所有的浏览器字体统一,就需要通过css加载远程字体,下面有个不错的示例,大家可以参考下,希望对大家有所帮助     复制代码 代码如下: <style type="text/css"> @font-face { font-family: 'C39HrP72DlTt'; src: url('C39HrP72DlTt.eot'); src: local('C39HrP72DlTt'), url('C39HrP72DlTt.eot'), url('C39HrP72DlTt.

在css加载完毕后自动判断页面是否加入css或js文件_jquery

最近在写项目的framework,写个JQueryMessageBox的类,以使用jquery ui中的dialog()来显示消息框,为了使方法方便调用,便加入了自动判断页面是否加入了ui.js和ui.css,代码如下: //如果没有包含ui.js,则引用 if ($('script[src$=""jquery-ui-1.8.11.custom.min.js""]').length == 0) {{ $(""<script src='/js

singtop-安卓的栈顶模式,创建活动后就位于栈顶,加载新的活动后再加载第一个创建的不显示位于栈顶。

问题描述 安卓的栈顶模式,创建活动后就位于栈顶,加载新的活动后再加载第一个创建的不显示位于栈顶. 首先创建两个活动,FirstActivity和secondActivity, 创建两个布局first_layout和second_layout 两个布局中都添加button按钮 在FirstActivity写一个意图指向SecondActivity 证明每个活动都有Log.d 在一般的情况下 在快速点击的时候出现了下面的情况 singleTop是位于栈顶就不会创建,理论上我的点击,怎么不会显示第一个

微信页面css加载-微信页面个别手机css加载不出来问题

问题描述 微信页面个别手机css加载不出来问题 用手机打开微信推广页面.有的手机加载页面出现css没有加载进来.页面里的图片文字还有js都 加载完整.有的手机可以正常显示. 有大神知道什么原因的吗?怎么解决 解决方案 http://zhidao.baidu.com/link?url=CGgVKSdy-T1DKU5QLxABTQ9JVtFGVKz_PstQEY5QnEFCsEI-ozXVHmYT4JJ6rqCvtKLzeekb-jKRz8rzsmoS_JJmHFmf5FkwxQM8629aP9i

Swift表格tableView上拉加载新数据的功能实现例子

对于表格(tableView)来说,下拉刷新数据.上拉加载数据应该是两个最常用的数据更新操作了.对于前者,我原来写过一篇相关的文章:Swift - 下拉刷新数据的功能实现(使用UIRefreshControl).本次我来讲讲后者的实现. 说是上拉加载数据,其实就是当我们将表格内容滚动到最后一行时,系统就会自动获取新的内容并添加到列表尾部(具体效果可以参考百度贴吧的App).下面我们通过一个小样例来演示上拉加载的实现. 1,样例效果图 (1)当初次进入程序时,先加载前20条数据. (2)当 tab