上一周我在Ajaxian中看到了一篇关于 @font-face 的文章。对于字体文件如何影响web网页效率的问题我已经思考了几个月了,于是我继续读了几篇文章,终于形成了下面这些自己的看法。
FOUT:Flash of Unstyled Text
Paul 将Flash of Unstyled Text指为FOUT。这也是对于 David Hyatt 提出的FOUC( flash of unstyled content )的一种继承。FOUC会在样式表加载于页面底部的情况下发生。
Paul 指出了两种类型的FOUT:
你可以通过我的 FOUT 实验 来观察,实验中的字体文件会耗费6秒的时间下载。IE下整个页面都是空白的,直到6秒后,即使HTML喝其他页面元素都已经在浏览器里了。
所有的这些FOUT情况对用户体验都是很糟糕的。它会造成用户浏览页面时的中断,或是信息突然改变。同时这也不只是 @font-face 文字的问题,如果默认字体和 @font-face 指向的字体大小不同, 所有的除IE以外的浏览器将会对页面其他部分重渲染。
FOUT并不会对页面的绝对加载时间造成影响,但它却会影响用户对于页面加载速度的印象,而这一点正是我们真正希望优化的。
阻滞其他下载
对于字体文件的下载是否会阻滞其他文件下载的问题,我们的答案很简单——不会。我使用了这样一个阻滞实验来判断字体文件是否会对其他资源的下载造成影响。测试页面包含了一些使用@font-face的文字。文字后面是一张图片,一个iframe,一张样式表,还有一个外联脚本。所有的文件都被设置为2秒的下载时间。在所有主流浏览器中,页面会在两秒内加载,表明并没有文件被阻滞。
注意字体文件也会受到同一域名下载限制的影响。试一试同域测试 就可以发现这一限制。在阻滞试验中我将资源分散到不同的域名下从而防止任何的链接限制。但是如果字体文件与其他资源位于同一域名下,阻滞将会由于同域下载限制而发生。
为了让不同域的字体文件能够正常工作于firefox下,你必须增加 Access-Control-Allow-Origin 的http头。到跨域测试中查看这一现象。
浏览器忙的标识
尽管字体文件不会阻滞其他文件的下载,它们仍然会影响到浏览器忙的标识。这会给用户一种页面需要很长时间加载的不良印象。浏览器忙的标识对于各个浏览器都不同,参见下表
同时要注意的是,在IE和firefox中,字体下载将阻滞window.onload事件,而Safari 和 Chrome将不会。 这个可以从第一个实验中看出。
下载浪费
Paul指出,Garrick曾经发现IE在下载字体的时候会有一小点不正常。IE在发现字体文件的声明的时候就开始下载。这意味着即使没有任何元素使用了该字体,IE也会下载这个字体文件。
同样你可以从我的 未使用字体测试 来验证。该页面拥有一处 @font-face的声明,不过页面中并没有使用。同时,该文件被控制需要6秒时间下载。显然,整个页面在IE中花费了6秒的加载时间,证实了即使没有使用字体文件IE也会下载的事实。也许IE希望这一点能够解决FOUT的问题,但是类似于这样的问题将导致很多的资源浪费,并且降低页面的性能。
压缩
Stoyan的文章指出字体文件必须要gzip。Paul开始认为这不可能,我也有类似观点。谢天谢地Stoyan指正,事实上,你必须压缩字体文件。他的研究显式这对于TTF,OTF和EOT文件将节约40%的体积。我现在将我的apache配置为压缩字体文件了。
缓存
与其他资源一样,字体文件可以被缓存。你可以在缓存测试中看到这一点。字体文件会在6秒后下载,所以当第一次你访问这个页面,加载将超过6秒。但是字体文件拥有了一个很长的过期时间,所以再次点击上面的链接,你会发现由于字体文件是从缓存中读取的,加载速度会快很多。
@font-face的连接问题
由于字体文件引发了FOUT和浏览器繁忙的问题,我们必须考虑最坏的情况: 如果字体文件的服务器发生了某些意外比如请求超时或者花费了很长的时间响应,会发生什么呢? 连接测试 展现了这一现象,而且结果不乐观。字体文件被配置为20秒后下载,下面是测试的结果:
由Annie的提示,我试着寻找浏览器花费在下载字体文件上的最大时间。我测试出的最长时间是10分钟。Safari 做的最好:60秒后它放弃了下载,选用默认字体渲染。IE出现了最坏的情况,10分钟的下载时间内,IE一直都展现的一张白页面。Firefox会立即使用默认字体渲染文字,但是浏览器忙的标识会一直持续10分钟。Chrome没有渲染文字,同时它的繁忙状态也持续了10分钟。
这些响应慢的问题使得考虑使用@font-face的开发者要考虑更多。它与图片的加载超时不同,那时候浏览器将以显式一个坏链图标来结束。如果一个字体文件不能下载,页面加载会在IE下阻滞,文字在Chrome下将不会显式,同时浏览器忙的标识将一直出现在IE,Firefox和Chome下(至少10分钟)。
(注意浏览器对于字体文件超时的行为和图片类似。)
@font-face 性能的建议
我首先的建议是除非它对于页面是非常重要的,否则不要使用@font-face。
最主要的原因是由于如果@font-face声明之前有script标签的话,字体文件的下载将阻滞整个页面的加载。样式表同样也有这样的阻滞问题。但是样式表是对于整张页面提供样式的,而字体文件仅仅加入了一个自定义的字体。
如果你仍然要使用@font-face,我建议在页面全部加载完毕后再下载字体文件,如 后加载测试 中一样。 这将解决IE中的问题——整张页面渲染之后,字体才会再后台下载,并在下载完成后增强我们的样式效果。这个技巧在其他浏览器中也同样有好处。使用后加载,大部分的浏览器将都不会出现浏览器忙的标识。后加载的代码大致如下:
1 function lazyload() {
2 var sRule1 = 3 "@font-face {" + 4 " font-family: 'Yanone';" + 5 " src: url('/bin/resource.cgi?type=font&sleep=6');" + 6 " src: local('Yanone'), " + 7 "url('/bin/resource.cgi?type=font&sleep=6') " + 8 "format('truetype');" + 9 "}"; 10 11 var style1 = document.styleSheets[0]; 12 if ( "function" === typeof(style1.insertRule) ) { 13 // Firefox, Safari, Chrome 14 style1.insertRule(sRule1, 0); 15 } 16 else if ( "string" === typeof(style1.cssText) ) { 17 // IE 18 style1.cssText = sRule1; 19 } 20 }
注意这只是个原型,并不是一个完美的解决办法。
在Paul的文章中提到了预读取字体文件,不过需要注意的是这个技术并不适合IE。我认为IE将是最难解决的问题,我同时希望能够将样式表、脚本文件和图片的优先级提高。这取决于页面以及字体文件的使用。
我不建议在样式表中使用 data:URIs 来定义字体文件。由于样式表可能包含了EOT和TTF两种格式的数据,这可能会让下载的数据加倍。这同样也会另样式表的加载时间增长,而在大部分浏览器中样式表会阻断页面加载。