07月26, 2016

在响应式网站中,提升加载webfonts的性能

前言

因为 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. Demo页面:从Google Fonts中加载字体
  2. Page Speed的测试:从Google Fonts中加载字体
  3. Demo页面:异步加载字体并且存储到localStorage中,第二次直接从localStorage中取出字体
  4. Page Speed的测试:异步加载字体并且存储到localStorage

页面访问截图

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就行)。

gfonts-timeline

这个timeline说明在DOMContentLoaded事件之前,字体的加载已经开始了。

(5)如果上面的前面还不够糟糕,大多数浏览器将呈现空白文本,在不同浏览器之间实际的行为会有所不同:

A. 如果请求字体还不可用,IE 会立即使用后备字体呈现,并在字体下载完成之后马上重新呈现;

B. Firefox 和 Chrome 35+ 会首先下载3秒钟的字体,如果超过3秒钟后,会使用后备字体渲染网页,等到指定字体下载完成后再重新渲染网页;

C. Safari 和 Chrome 35之前的版本,会等到指定字体下载完成后再渲染网页(PS:就是不会使用后备字体)。

注:以上说明中没有表示IE的版本以及Safari的版本号,所以需要自己测试才能算正确。

因此,如果网络连接缓慢,在大多数浏览器中将延迟超过3秒的文本渲染。在最坏的情况下,如果你的字体加载带有时间限制(由于一些连接很慢的移动设备),Safari 用户将不会再展示文本,剩下一个空白网页。如果网页请求超时,最终将只会呈现一个空白网页。

更多的信息可以访问:Ilya Gregorik's blog

其他相关资源:

其实英文版本的作者也是翻译的一篇俄文,这篇俄文来自于:

Russian by Максим Усачев

英文版本原文出自:

http://bdadam.com/blog/loading-webfonts-with-high-performance.html

注:如果你有兴趣可以再看一下英文版本和俄文版本文章下面的评论,里面还有一些很有价值的东西,这里就不再翻译了,有时间可以再整理一下。以上文章如果有翻译不恰当的地方请在文章后面留言,看见了一定回复并且修改。

原文出自:http://www.60sky.com

本文链接:http://www.60sky.com/post/relative-site-web-fonts.html

-- EOF --

Comments