前言
因为 Arial, Verdana, Garamond or Times New Roman 这些字体几乎在所有电脑上都有安装,所以以前每个网站都使用他们来渲染文本。至今,虽然webfonts已经在互联网中得到普遍的使用,但是我们依然不知道如何高效的加载他们。
为了提供更好的用户体验,我写了一些怎么做才能高效加载webfonts的简单解决方法,这个方案的实施并不需要昂贵的硬件支持。
0. 目录
主要从以下方案进行介绍:
(1)使用woff字体格式(PS:可能是EOT和TTF格式默认情况下不会进行压缩,然而WOFF格式具有内建压缩,而且WOFF格式的支持很广泛的原因。);
(2)对于不支持webfonts的浏览器使用“web安全”字体(PS:使用前言中提到的一些几乎所有电脑都安装了的字体作为后备字体,保证用户能够正常浏览网页);
(3)下载字体的“二进制”格式,并且优化它;
(4)使用你自己的字体库;
(5)将字体进行base64编码后,存放于CSS文件中;
(6)如果用户本地没有网页请求的字体,那么就异步加载该字体,并且将该字体存储在localStorage中,以便第二次请求字体直接从localStorage中进行读取,避免字体加载带来的性能问题;
英文版本的作者 2014/10/09 更新内容
如果你不相信以上方案能够优化网页的性能,那么我创建了2个Demo页面。能够测试他们在:资源加载、资源阻塞以及其他方面的问题。
1. 浏览器支持程度
通过 caniuse 的统计,有84%用户的浏览器支持WOFF字体格式。除此之外,不支持WOFF格式的浏览器包括:IE8及其以下、Android上的一些老浏览器。因此,这里提供的优化是针对于支持WOFF字体格式的现代浏览器的方案。对于旧浏览器依然使用后备字体来展现网页(比如:Arial = = PS以下:中文的话可以采用“simsun”,属于衬线字体),这将给用户带来更好的用户体验。
2. 不要使用Google Fonts或者Typekit这类的外部链接方式
这两种方式会造成以下两个问题:
(1)额外的请求阻塞
(2)通过它们异步加载字体的时候会出现闪烁问题。
下面我们将看到处理webfonts更好的方式。
3. licence问题
选择一个webfonts自己使用。不幸的时没有任何一个licence允许这么干。不过值得庆幸的时我们可以利用一些开源的字体,比如:Open Sans、Source Sans Pro。当你发现你想要的字体就能够下载它们的“二进制”文件(OTF或者TTF)。
4. 优化、减小字体文件大小以及生成CSS文件
这里推荐一个网站:Font Squirrel Webfont Generator
(PS:这个网站是用来处理字体的,有捐赠环节的哦~具体功能可以点进去尝试一下就知道了)
我们能够选择一些额外的方式来移除一些字符。你能够选择你需要的一些字符出来使用。如果你的网页全是英文内容,那么你只需要选择一些基本字符;如果你得网页有中文,那么你可能需要所有的字符。(原文错误:Chineese -\> Chinese)
更重要的是最后生成我们所需要的,包含了字体base64编码信息的CSS字体文件。
5. 使用字体的CSS文件
该CSS文件的大小取决于你选择的字符集合以及相关方面,也许该文件相当的大(最高可达100~300KB)。因此,使用gzip压缩以及设置强缓存的方式对于用户来说是很重要的。
不过幸运的是只有当你网页的浏览者第一次访问该CSS文件的时候会发出请求。由于在第一次的时候,用户本地没有该字体文件,所以浏览器就会去异步加载他们,并且存储在localStorage中。当用户的网络环境较慢的情况下,能够看到后备字体以及webfonts渲染过程,不过这些只会发生在用户第一次访问你网页的时候。大多数用户不会太在意这一细节。
当用户第二次网页页面的时候,浏览器将从localStorage中加载CSS文件内容,这种方式相当的快速(5~50ms)。在这种情况下用户看不到任何的闪烁,因为所有的操作将是同步进行的,这仅仅只需要几毫秒的时间。
6. 展示一下代码的编写
由于我使用的是localStorage技术,所以只有客户端的代码。
(function(){
function addFont() {
var style = document.createElement('style');
style.rel = 'stylesheet';
document.head.appendChild(style);
style.textContent = localStorage.sourceSansPro;
}
try {
if (localStorage.sourceSansPro) {
// 如果localStorage中有该字体,就直接取出来加载
addFont();
} else {
// 首次加载字体我们需要异步加载它
var request = new XMLHttpRequest();
request.open('GET', '/path/to/source-sans-pro.woff.css', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// 保存到localStorage中,key=sourceSansPro
localStorage.sourceSansPro = request.responseText;
addFont();
}
}
request.send();
}
} catch(ex) {
// 这里处理一些同步加载woff功能的浏览器
// 避免当localStorage不可用的时候,那么将每次请求字体带来的闪烁问题
}
}());
优化的流程图简图如下:
7. 我们做到了什么
(1)解决了除用户第一次访问网页外,其余更多次访问时候的请求阻塞问题;
(2)解决了除用户第一次访问网页外,其余访问的闪烁问题;
(3)减少了第一次请求页面的渲染时间;
(4)得到了在Google Page Speed Insights 和 WebPageTest.org 上更高的分数。
8. 看看实际效果
英文版本的作者说到本文缺少一些细节的说明,能够在他博客中留言讨论。
英文版本的作者 2014/10/11 更新内容
通过内联Goole提供的CSS文件的方式,在Google Page Speed Insights中取得了99/100的分数。
英文版本的作者不赞同使用这种方式,因为这种方式会严重影响文本的渲染,因此我们来深入了解一下其中发生了什么。
(1)首先我们定义一个内联的font faces。
<head>
...
<style>
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'),
local('SourceSansPro-Regular'),
url(http://fonts.gstatic.com/s/sourcesanspro/v9/ODelI1aHBYDBqgeIAH2zlBBHWFfxJXS04xYOz0jw624.woff) format('woff');
}
</style>
...
</head>
(2)由于浏览器最开始不知道页面哪一个地方会使用到该字体,所以不会去请求这个字体文件。
(3)浏览器要等待DOM和CSSOM构建完成。
(4)浏览器这时开始从Google Fonts请求字体文件,需要注意的是,这里会有一个来自于fonts.gstatic.com的额外DNS请求(PS:在国内的环境就不要想google字体的事情了,可以使用360的公共CDN服务,很好用,改改URL就行)。
这个timeline说明在DOMContentLoaded事件之前,字体的加载已经开始了。
(5)如果上面的前面还不够糟糕,大多数浏览器将呈现空白文本,在不同浏览器之间实际的行为会有所不同:
A. 如果请求字体还不可用,IE 会立即使用后备字体呈现,并在字体下载完成之后马上重新呈现;
B. Firefox 和 Chrome 35+ 会首先下载3秒钟的字体,如果超过3秒钟后,会使用后备字体渲染网页,等到指定字体下载完成后再重新渲染网页;
C. Safari 和 Chrome 35之前的版本,会等到指定字体下载完成后再渲染网页(PS:就是不会使用后备字体)。
注:以上说明中没有表示IE的版本以及Safari的版本号,所以需要自己测试才能算正确。
因此,如果网络连接缓慢,在大多数浏览器中将延迟超过3秒的文本渲染。在最坏的情况下,如果你的字体加载带有时间限制(由于一些连接很慢的移动设备),Safari 用户将不会再展示文本,剩下一个空白网页。如果网页请求超时,最终将只会呈现一个空白网页。
更多的信息可以访问:Ilya Gregorik's blog
其他相关资源:
其实英文版本的作者也是翻译的一篇俄文,这篇俄文来自于:
英文版本原文出自:
http://bdadam.com/blog/loading-webfonts-with-high-performance.html
注:如果你有兴趣可以再看一下英文版本和俄文版本文章下面的评论,里面还有一些很有价值的东西,这里就不再翻译了,有时间可以再整理一下。以上文章如果有翻译不恰当的地方请在文章后面留言,看见了一定回复并且修改。
原文出自:http://www.60sky.com
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。