whexy1999

博客字体的解决方案

Whexy /
February 21, 2022
22 min read

从初中入手 Kindle 电子书以来,我不停更换阅读字体以追求舒适的阅读体验,期间也研究过不少字体嵌入和排印的知识。在设计博客页面时,我带着对书籍般的苛刻标准选择适宜屏幕阅览的字体。

目前博客使用的字体:

衬线字体的突围

之所以选择不常见的宋体(Serif 衬线字体)而不是黑体(Sans 非衬线字体),是因为宋体更具有沉浸感。书籍、排印作品喜欢用宋体。

系统和应用程序最喜欢使用黑体(你可以检查一下手边设备的菜单栏、控制中心、APP 抽屉),因为它方便在屏幕上快速阅读。随着智能设备对人的 “驯化”,我们的大脑已经足够适应黑体,并且能排除掉用它显示的冗余信息。阅读黑体显示的文本时,你很容易走神,或者飞快的滑走 —— 这是因为我们平时就是这样使用手机和电脑的。

黑体示例:iOS 设置界面
黑体示例:iOS 设置界面

仿宋适合打印文稿、公共文书,适合大段废话。最鲜活的例子是:标准红头文件的正文部分使用仿宋1。自从红头文件改用仿宋之后,我对仿宋就提不起兴趣。

仿宋示例:红头文件
仿宋示例:红头文件

我的博客通常写得很长,而且信息熵较高,比较适合用宋体阅读 😊。

思源宋体

多数人对于宋体的不适感主要来自于 Windows 自带的丑陋宋体。下图是几种常见字体的比较,相信你看完后应该能理解为什么苹果(使用苹方及 macOS 宋体)比微软(使用雅黑及 Windows 宋体)更受欢迎。

字体比对
字体比对

思源宋体是所有宋体之间最丰富的。它能完美显示同一个汉字在不同语言中的变种(简体中文、繁体中文、日语、汉语),还支持七种字重。

思源宋体的七种字重
思源宋体的七种字重

自从 2017 年思源宋体推出,它就广受好评。下面引用专业设计师的评论。

对比 macOS 系统内置的宋体,思源宋体不但字重更丰富,并且字面更大,易读性稍好。Windows 内置宋体直接负分出局。相比思源黑体这种非衬线字体,思源宋体更适合正式的文档,感觉更庄重。

Roboto Serif

前文也提到,经过几年对字体的探索,我的结论是:在电子屏幕上,合适的宋体(Serif 衬线字体)比黑体(Sans 非衬线字体)更具有沉浸感。

无独有偶,在 2022 年 2 月 18 日,Google 团队推出了英文衬线字体 Roboto Serif。字体设计师 Gazdowicz 介绍称:“我们的目标是打造一款你能用来阅读长篇报道或小说的字体,就那种特别长篇,需要投入其中的文体。”2

「长篇」和「投入其中」正是之前我给中文字体定下的基调。那英文字体果断选择 Roboto Serif。

可见谷歌的设计师和我水平差不多啊😆。

根据 Google 的说法,这个新字体凭借可变的字体设计,可以为不同尺寸的显示器自动改变和优化字母形状。下面引用知名媒体 The Verge 的新闻稿。

The new font still uses the same vertical proportions of Roboto Sans, making it possible to mix the serif and sans-serif versions in a single design. It probably also helps that chunkier, retro-styled serif typefaces are coming back in style in a big way after years of minimalist san-serif designs dominating, as noted by Vox. Serif fonts are also considered to be easier to read, thanks to the more distinct letter shapes, something that Google’s new font expands on by virtue of being a variable font that can automatically change and optimize the letterforms for different size displays.

在博客中使用字体

说了这么久,终于到了本文的正题部分 —— 我是如何在博客网站中使用这两款字体的?

无论是中文的思源宋体,还是英文的 Roboto Serif,它们都有丰富的字重。思源宋体的字体粗细可以分出 7 档,而 Roboto Serif 更是可以分出无数档(原理见 Variable Fonts)。

字重丰富,字体的大小不容小觑。思源宋体一个字重大约有 2MB,7 个档位接近 14MB。Roboto Serif 字体虽然只有英文字母,但也高达 2.4MB。在博客中使用这些字体,如果全部塞进页面,则需要在正文加载之前预先传输 16MB 的数据。

📱

16MB 是什么概念

直至 2022 年,中国移动仍有 5 元 30MB 的套餐。也就是说这些用户需要支付 2.5 元的流量费用才能浏览一篇文章。开个玩笑,不会真的有人用这个套餐吧?

16MB 在 5G 时代是一个非常小的数字。按照 5G 峰值速度(2.5GB/s)来算,只需要 0.00625 秒就能下载完。(所以大家应该积极地更换 5G 手机。)

当然,最终我们还是要考虑到实际情况。根据工信部发布的《中国宽带发展白皮书》3,大陆地区的手机平均下载速度为 3.74MB/s。这些字体加起来需要 4.28 秒才能下载完毕。又根据 wpostats 的报告,如果网页加载时间从 1 秒增加到 3 秒,跳出率就会提高 32%;如果网页加载时间从 1 秒增加到 6 秒,跳出率就会上升 106%4

光字体就 4.28 秒,加上 React+NextJS 离谱的首次加载时间,我会损失超过一半的阅读量。加载时间过长会影响搜索引擎的收录,进一步损失阅读量。

可访问性:Vercel 曾经给我打出 48 分的低分
可访问性:Vercel 曾经给我打出 48 分的低分

此外,巨大的首次加载数据量也会影响网站成本。按照一个用户访问两篇博客计算:如果日均访问量是 1000,服务器一个月需要缴纳将近 1TB 的流量费用。我做博客一没有广告,二没有捐赠通道,纯粹用爱发电。钱包在滴血!

总结一下,如果想要使用这两个字体,需要解决两个关键问题:

  • 字体过大造成的用户体验问题
  • 服务器流量成本问题(不管字体大小,钱应该是能省就省的)

字体优化

你可能期待我给出什么精妙的解决方案。我没有。不知道是谁曾经说过(或者我是第一个):所有的工程问题都是权衡问题(trade-off)。

首先解决字体过大的问题。中文字体有 14MB,英文字体只有 2MB。重点在于缩减中文字体的大小。

中文字体与英文字体最大的不同,在于英文字体只有 26 个英文字母和一些标点符号,而中文有海量汉字。一篇英文博客基本上能把 26 个字母都用到,字体的利用率接近 100%。而中文博客讲来讲去也用到几百个汉字,字体的利用率不到 1%。

为了解决字体利用率的问题,我找到了腾讯做的一个叫 “字蛛” 的项目。它可以根据实际使用的汉字生成对应的字体文件,从而极大程度上减低中文字体的大小。

接下来解决服务器流量成本问题。按说减轻了字体大小,成本就已经降下来了。但是苍蝇腿再细也是肉,积少成多,我还是要付出额外的成本支撑字体载入。博客挂载在 Vercel 提供的免费托管服务上,但我为了提升长城保护区(作者注:即 CCP 实际控制区域)的访问速度又额外套了一层 Microsoft Azure CDN。这 CDN 的钱是按流量收的。尽管每个月只有几美分 😅(便宜到爆),但是我就是不想交这个钱~

托管字体

互联网带善人 Google 又来做好事了。从 2011 年开始,Google Fonts 服务将源字体托管在 Google,免费提供给各网站使用。不但能节省我们的带宽,还可以让我们将大量字体免授权地运用于个人或商业项目。

我给博客的定位是「混杂着英文的简体中文博客」。很遗憾,使用简体中文的地区大多都不能访问 Google Fonts 服务。为了多数人的博客阅读体验,我只好尝试使用「国内特供版」字体托管服务。

不知道是不是由于版权原因,曾经那些大厂(诸如 360、七牛 等)提供的字体托管服务相继关闭。剩余的服务提供商或多或少都有些问题。

  • 名不见经传(名字叫 “有字库”“就是字”,很难相信它们能提供免费服务)
  • 官网访问都很慢,多地区访问速度更是堪忧
  • 我担心它们某天突然跑路

一圈调研下来,「特供版」要么不能用,要么不敢用。于是我去实验了曲线救国的方案。

反向代理?

据我了解,中科大 LUG 社团曾经制作过 Google Fonts 的反向代理,用于解决 LUG WordPress 站点的字体问题。地址是 http://fonts.lug.ustc.edu.cn

现在已经是 2022 年,LUG 社员换了一批又一批,不知道这个没有官方文档的反向代理还能不能用。所以我试着追踪了一下。

❯ http http://fonts.lug.ustc.edu.cn
HTTP/1.1 301 Moved Permanently
Location: http://fonts.proxy.ustclug.org/
Server: nginx

❯ http http://fonts.proxy.ustclug.org/
HTTP/1.1 301 Moved Permanently
Cache-Control: max-age=3600
Cf-Ray: 6e08b090ea4c24bf-HKG
Location: https://fonts.googleapis.com/
Nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}

不出所料,早就不能用了。不过 LUG 还算靠谱,没有直接删库跑路,而是通过两层永久重定向指向了 Google 的服务器,保证了在长城保护区以外其他地区的可用性。(第一层从中科大服务器跳到了 LUG 自己的域名,第二层直接通过 Cloudflare 的域名规则跳转到 Google Fonts)

🤔

自己搭?

虽然 LUG 关了,但是它们的思路还是有道理的。也许我可以自己搭一个反向代理给自己用?看着手上的细水管小鸡 (Oracle 云服务免费服务器) 瑟瑟发抖,我实在于心不忍。此外,Google Fonts 现在使用 JavaScript 脚本分块加载字体,利用并行请求加速字体传输。一个中文字体可能分出十几个 chucks。而 chucks 的链接被硬编码到它的脚本中。浏览器加载脚本执行,直接绕过了反代。所以 LUG 停服也有它们的理由。

妥协

好像字体托管走到了死路上。而且我前文忘记说:字体托管服务不能解决字体过大导致的用户体验问题。托管服务托管的是整个字体,不能用字蛛缩减字体大小。

这两个问题你到底解决了啥?

别骂了别骂了。我们再回顾一下前文总结的两个问题:

  • 字体过大造成的用户体验问题
  • 服务器流量成本问题

之前也说到,所有的工程问题都是权衡问题(trade-off)。我们把现在面临的情况列举一下。

  • 字体过大造成的用户体验问题
    • 用腾讯字蛛解决
    • 不能使用字体托管服务
  • 服务器流量成本问题
    • 用字体托管服务解决
    • 不能减少字体大小
    • 部分地区无法使用 Google Fonts、「特供版」托管服务容易跑路、反代失效

你看,有的事情不能做就是不能做(四年级学生说不能做就是不能做)但是我能在两个问题之间找到一个平衡点,使得文件没那么大,用户体验尚可,而且不计入服务器流量。

减少字重的使用

反思一下,我写博客真的用得到 7 种字重吗?盘点了一下博客文章,总计使用过三个字重:普通(weight=300),加粗weight=700),以及小字体的极细weight=200)。所以我只需要这三种字重的字体文件即可。

使用 Google Webfonts Helper,可以提取出字体不同字重的文件。它还能生成对应的 CSS 样式表代码,方便添加到项目中。

Google Webfonts Helper 使用界面
Google Webfonts Helper 使用界面

使用对象存储(COS)

我的博客属于服务端渲染(SSR),不是静态网页生成(SSG)(关于这两者的区别,详见构建自己的博客系统)。比起博客网页内容,字体是一个纯静态的资源。不宜通过我的博客后端分发。这回我盯上了对象存储 COS。

对象存储 COS 是在云上提供无层次结构的分布式存储产品,为用户提供单价较低且快速可靠的数据存储方案。COS 以冗余的方式跨多个可用区存储用户数据,并允许多个不同的客户端或应用程序线程同时对这些数据进行读或写操作。用户可通过云服务器实例或互联网使用 Web API 接口存储和检索数据。在 COS 上的数据,用户使用指定域名的 URL 地址,通过 HTTP/HTTPS 协议存储和检索每个独立的数据对象内容。

…… 具备无文件系统、目录结构、文件数量和空间上限的特性,需通过 Web API 接口管理和访问存储,提供了 SDK 和工具等集成,可以不依托云服务器单独使用。对象存储支持大规模数据的访问,但不适合毫秒级响应或随机读写的场景。

什么是对象存储 COS?

腾讯云对象存储产品文档

正如腾讯云市场部的同学们所说,COS 是一个适宜托管静态资源的服务。存储静态资源便宜、而且可用性更高。去年之前我一直在用腾讯云 COS 做博客图床。

如果把云服务地域设定在香港地区,COS 有 50GB 的免费存储费用。外网下行流量为 0.75 元 / GB。为了防止其他网站白嫖,设定了 CORS,只允许我的域名使用。如果后续访问量上升,可以考虑再套一层 CDN。

对于已经获取的资源,浏览器会发送 OPTION 请求来判断资源是否有修改。我配置 Max-Age 为 7200 秒,即两小时(Chrome 支持的最长时间)。这样除非你浏览我的博客超过两个小时,字体文件都会在缓存中保存。

字体上传到 COS,把 Google Webfonts Helper 生成的 CSS 中的 url 改成 COS 的外链地址。

/* noto-serif-sc-regular - chinese-simplified */
@font-face {
  font-family: "Noto Serif SC";
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: local(""),
    url("https://fonts-1251112473.cos.ap-hongkong.myqcloud.com/noto-serif-sc-v16-chinese-simplified-regular.woff2")
      format("woff2"), /* Chrome 26+, Opera 23+, Firefox 39+ */
      url("https://fonts-1251112473.cos.ap-hongkong.myqcloud.com/noto-serif-sc-v16-chinese-simplified-regular.woff")
      format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}

一切准备妥当,字体就能投入博客网页使用啦。注意我加上了 font-display: swap 的设定,可以让浏览器在字体加载完成前使用系统默认字体,加载完毕后再替换。

找了几个朋友帮忙在各地测试,字体加载速度超级快,控制在一秒以内。

破事水

破事水是 “這點破事也拿出來水” 的縮寫,是一句網絡流行縮寫語。我写完这篇博客后也在怀疑自己「破事水」。这篇博客记录的不是步骤而是经历,分享的不是技术而是心情。

如果你喜欢这篇博客,或想第一时间获得通知,可以通过 RSS 订阅更新。也欢迎关注我的 GitHub 和推特账号。我平时是主要分享关于计算机系统安全的内容。如果你对这篇文章有任何疑问或建议,请在下方留言。再会~

平时看你分享的安全内容也不多耶
收皮啦你!!

Footnotes

  1. 宋体和仿宋到底有什么区别?- 知乎

  2. Google 做了款新字体,想让你舒服地「沉迷手机」| Feel Good 周报 | 爱范儿 (ifanr.com)

  3. 中国信通院 - 通信行业:中国宽带发展白皮书 - 210929.pdf

  4. "核心网页指标" 报告

© LICENSED UNDER CC BY-NC-SA 4.0