网页字体在英文站只占几十KB,到了小语种站就变成拖慢首屏的最大一块

网页字体在英文站只占几十KB,到了小语种站就变成拖慢首屏的最大一块
张文保 更新 35 分钟阅读 4,030 阅读
本文目录
  1. 同一个字体文件,换一种语言为什么会重好几倍?
  2. 一份拉丁字体里到底有多少个字形
  3. 换成非拉丁文字之后这个数怎么涨
  4. 体积不是线性关系,还要加上排版表
  5. 非拉丁文字的字形集大在哪几处?
  6. 一个字母四种呈现形态
  7. 连字与必需连写
  8. 上下叠加的记号需要定位表
  9. 合体字与音节组合
  10. 子集化在小语种上为什么不能照搬拉丁语的做法?
  11. 拉丁语可以按字符频率裁,非拉丁不行
  12. 删掉排版表会渲染错
  13. 数字属于另一个码位区间,最容易漏
  14. 组合记号被裁掉的后果
  15. 字体不加载的后果,在两类文字里不是一回事
  16. 拉丁文字回退到系统字体仍然可读
  17. 非拉丁文字可能直接回退成方框
  18. 闪烁与遮挡两种策略在这里的取舍
  19. 系统自带字体能不能省掉这个文件?
  20. 各平台对非拉丁文字的覆盖差别
  21. 低端设备上的覆盖更差
  22. 回退链要写几层、按什么顺序
  23. 首屏上真正被字体拖住的是哪几个元素?
  24. 标题、价格、导航三处的先后
  25. 图标字体是另一个被忽略的来源
  26. 正文字体可以延后,标题不行
  27. 格式与压缩能省下多少?
  28. 三种格式的体积差
  29. 压缩对不同文字的效果不一样
  30. 传输之外还有解析成本
  31. 分片在小语种上的边界怎么划?
  32. 按码位区间分片的原理
  33. 一个词跨两个分片会怎样
  34. 哪些语言适合分片、哪些不适合
  35. 分片带来的请求数问题
  36. 怎么量:一套按语言拆开的度量口径
  37. 全站平均值会骗人
  38. 按语言分面的四个指标
  39. 给每个语言版本单独定预算
  40. 哪些不归语言层,要交出去
  41. 通用的性能优化归技术层
  42. 移动端的判定与架构
  43. 字体授权与设计选型
  44. 常见问题解答
  45. 小语种站的字体文件到底比英文站大多少?
  46. 子集化能不能像英文站那样激进地裁?
  47. 让文字先用系统字体显示,这个建议在小语种站成立吗?
  48. 首屏字体该怎么拆才能既快又不难看?
  49. 按码位分片这个做法值不值得用?
  50. 为什么全站的性能数据看起来都正常?
  51. 预算有限时,这几件事该按什么顺序做?
  52. 权威参考资料

摘要:字体文件的体积不是一个固定值,它跟着语言走。一份拉丁字体装的是几百个字形,换成阿拉伯语要装上千个,换成印度诸语要装几千个,换成汉字要装上万个。字形数量之外还有排版表,连写、叠加记号、合体字各自需要一套规则数据。于是同一个设计、同一种格式,做英文站几十KB的文件,到小语种站变成几百KB甚至几MB。这篇把这笔账拆开算。

同一个字体文件,换一种语言为什么会重好几倍?

一份拉丁字体里到底有多少个字形

一个只支持英文的字重,大约需要两百到三百个字形。

大小写字母五十二个,数字十个,常用标点几十个。

再加上一批符号和几组常用的连写形式,就差不多了。

做成现代格式压缩之后,一个字重通常落在二十到四十KB。

要覆盖西欧语言的变音字母,字形数涨到五百上下。

体积跟着涨一半左右,仍在可以接受的范围。

这个数量级是很多性能建议的隐含前提。市面上大部分关于字体优化的经验值、预算建议和取舍结论,背后假设的都是这种规模的文件,直接搬到小语种站会得出完全错误的判断,因为分母根本不是一个量级。

这个数字还有个用处:它是判断一份字体是不是被人偷偷塞了多余内容的基准。有些商用字体为了通用性把多种文字打包在一份文件里,只做英文站的团队完全不知道自己下发了一堆用不上的字形。

换成非拉丁文字之后这个数怎么涨

西里尔字母加上去,再多两百多个字形。

希腊字母加上去,又是几百个,带重音的形态还要另算。

阿拉伯字母系统一旦加进来,数量直接跳到上千。

泰文、天城文这类文字,字形数在一千到两千之间。

汉字就完全是另一个量级,常用集就有几千个。

日文还要再叠上两套假名和大量专用符号。

把这几档排成一个梯度看会更直观:英文两三百、欧洲全覆盖五百、阿拉伯语一千五、泰文两千、韩文一万一千多个音节、汉字常用集六千起。每上一档,文件体积大致跟着翻一倍到几倍,这个梯度决定了各语言版本的性能预算不可能是同一个数。

要拿到自己这份字体的确切字形数不难,任何字体编辑工具打开都能看到。花五分钟把在用的每一份都数一遍,写进一张表,比引用任何外部经验值都可靠,因为设计繁简与覆盖范围各家差别很大。

这个梯度还解释了一个常见的困惑:为什么同一套设计规范落到不同市场,前端给出的排期差别那么大。字形集大的语言不只是文件重,配套的测试、子集验证和回退方案也全都更费工,工作量不是按语言数线性叠加的。

体积不是线性关系,还要加上排版表

字形数据只是文件的一部分,另一部分是排版规则表。

这些表告诉渲染引擎哪些字符要替换成哪个形态、位置怎么调。

拉丁文字用到的规则很少,表基本可以忽略不计。

阿拉伯文字要靠这些表决定每个字母用哪个形态。

泰文和天城文要靠它们把元音记号放到正确的位置。

所以这些表在非拉丁字体里占的比例相当可观。

这一点直接推翻了一个常见的直觉:以为只要把用不到的字形删掉,文件就会瘦得跟拉丁字体差不多。规则表跟字形不是一一对应的,删掉一半字形并不会让表也小一半,某些情况下表根本不能动。

还有一类数据也常被忽略:字距调整表。它记录成对字符之间的间距微调,对数量的规模是字符数的平方级别,覆盖多种文字的字体里这张表可以大到跟字形数据同一个量级。

非拉丁文字的字形集大在哪几处?

一个字母四种呈现形态

阿拉伯字母系统里,字母的形状取决于它在词里的位置。

独立时一种,词首一种,词中一种,词尾又是一种。

二十八个基础字母,光形态就要几十上百个字形。

再算上波斯语等语言追加的字母,数量继续往上走。

这些形态在编码里有专门的呈现形式区做过映射。

现代做法是只存基础字符,形态由排版表在渲染时决定。

为什么会有那个呈现形式区,原因在早期:那时的渲染系统不具备实时替换的能力,只能把每一种形态都当成独立字符编进去。呈现形式区的码表今天主要用于兼容老数据,字体里通常不再需要为它们单独造字形。

形态替换是渲染时实时完成的,这意味着渲染引擎的实现质量直接影响结果。同一份字体在不同浏览器上的连写表现可能有细微差别,验收时至少要覆盖目标市场占比最高的两个浏览器内核。

连字与必需连写

有些字母组合在书写传统里必须连成一个整体。

这类组合不是可选的美化,是正字法的要求。

每一个必需连写都要在字体里造一个独立的字形。

常见的必需连写有几十组,讲究一点的字体会做上百组。

如果字体里没有,渲染出来的文字本地人一眼就看出不对。

这部分字形没有任何删减空间。

拉丁字体里也有连写,但那些几乎全是可选的排版效果,删掉只是少了点精致感,读起来完全没问题。两者的性质完全不同,做取舍时不能用同一个标准去衡量,这是子集化最容易出事的地方之一。

判断某个连写是必需还是可选,最可靠的依据是这门语言的正字法文献,其次是本地主流出版物的排版惯例。凭感觉判断很容易把必需的当成可选的删掉,而这类错误在本地用户眼里相当刺眼。

上下叠加的记号需要定位表

泰文的元音和声调符号写在辅音的上方和下方。

叠加的层数最多可以到两层,位置要精确。

不同辅音的高度不同,记号的落点要跟着调整。

这套调整规则全部写在定位表里。

定位表被裁掉的话,记号会重叠或者飘到错误的位置。

越南语的双记号叠加也依赖同一类规则。

这跟带声调符号的文字里记号写在哪个元音上是一体两面:正字法规定记号该在哪,定位表负责把它画在哪,两者对不上时页面上呈现的就不是用户预期的那个词。

定位表出问题时有个特征可以帮助识别:错误是系统性的而不是随机的,所有相同结构的字符都错在同一个方向上。看到整页的记号都偏高或者偏左,基本可以确定是表的问题而不是个别字形缺失。

合体字与音节组合

印度诸语里,辅音连缀会合成一个新的字形。

两个辅音合一个,三个辅音再合一个,组合数量很大。

做得完整的字体要造上千个合体字形。

韩文的情况不同,它是把字母拼成方块音节。

理论上的音节组合有一万多个,实际常用的也有两千多。

字体可以选择存组件动态拼,也可以存现成的音节。

两种做法的取舍很有意思:存组件文件小但对渲染引擎要求高,存音节文件大但兼容性好。低端设备上后者更稳,而低端设备恰恰是这些市场的主力机型,于是体积和稳定性之间的矛盾在这里格外尖锐。

选哪种做法还要看内容的实际用字范围。如果站上的内容只涉及商品名和常规文案,实际用到的音节数远小于理论组合数,存现成音节的方案配上子集化,体积可以压到比想象中小得多。

合体字数量还会影响一件容易被忽略的事:字体加载失败时的降级表现。缺了合体字形的渲染结果不是方框而是拆开的单个辅音,本地用户读起来像是把词拼错了,比直接显示方框更容易被误解成内容质量问题。

子集化在小语种上为什么不能照搬拉丁语的做法?

拉丁语可以按字符频率裁,非拉丁不行

拉丁文字的做法是统计页面上实际用到的字符再裁。

因为字符和字形基本一一对应,裁掉就是省掉。

非拉丁文字里,一个字符可能对应四五个字形。

裁字符不等于裁字形,还要判断哪些形态会被用到。

而形态会不会被用到,取决于这个字符出现在词的什么位置。

词的位置又是内容决定的,静态分析算不准。

稳妥的做法是保留某个字符的全部形态,只在字符层面做裁剪。这样省下来的空间比拉丁文字少得多,但至少不会渲染错。想再往下压就必须实测,拿真实内容渲染一遍比对结果,光靠工具的默认设置不够。

动态生成子集是另一条路:服务端按页面实际内容实时生成对应的子集文件。它的省流量效果最好,代价是缓存基本失效,每个页面都要下一份新文件,对多页面浏览的电商站通常得不偿失。

删掉排版表会渲染错

子集化工具默认会保留用到的排版规则。

但工具的判断依据是输入的字符集合,不是真实内容。

字符集合里少一个字符,相关的连写规则可能被整条删掉。

结果是大部分文字正常,个别词的连写没了。

这类错误极难被发现,因为页面整体看起来是好的。

只有懂这门语言的人才会注意到某个词写得不对。

防这类错误有个实用办法:准备一段专门用来测试的文本,把已知需要连写的组合都塞进去,每次子集化之后渲染这段文本截图比对。这段文本一次准备好可以长期复用,成本极低。

子集化工具的选项里通常有一个保留全部排版规则的开关。在弄清楚具体影响之前,先把这个开关打开是明智的,省下的那点体积远远抵不上渲染出错的代价,等测试体系建起来之后再考虑收紧。

数字属于另一个码位区间,最容易漏

不少语言有自己的一套数字符号。

它们在编码里位于跟字母不同的区间。

子集化时如果只按字母范围取,数字就整组丢了。

丢了之后价格和规格全变成方框,后果非常明显。

好在这类错误一眼就能看见,不会静悄悄地留在线上。

把数字区间显式写进子集配置,一劳永逸。

波斯语和阿拉伯语各有各的数字区间,两套都要收进去,这跟波斯语与阿拉伯语在数字形态上的分叉是同一件事的两个侧面:内容层要处理查询归一,字体层要保证两套都画得出来。

同样容易漏的还有标点。不少语言有自己的逗号、问号和引号形态,它们跟拉丁标点是不同的码位。漏掉之后正文里会零星出现方框,比整段乱掉更难被发现,因为量少且分散。

组合记号被裁掉的后果

有些字符在存储时是基字母加一个独立的记号。

渲染时靠定位表把记号叠到基字母上。

如果记号字形被裁掉,屏幕上会出现基字母加一个方框。

如果定位表被裁掉,记号会画在错误的位置。

两种情况用户看到的都是错的,但表现完全不同。

排查时先看是缺字形还是缺规则,两者的修法不一样。

这里还要注意存储形式的问题:同一个带记号的字符可能以预组合形式存,也可能以基字母加记号的分解形式存。子集配置里只写了预组合形式的话,分解形式的内容照样会缺字形,两种形式都要覆盖到。

要覆盖两种存储形式,最简单的办法是把内容先做一次统一的规范化处理再统计字符集。这样统计出来的集合是确定的,子集配置也就跟着确定了,而不是依赖内容当时恰好用了哪一种写法。

字体不加载的后果,在两类文字里不是一回事

拉丁文字回退到系统字体仍然可读

拉丁文字的系统字体覆盖是完整的,任何设备都有。

自定义字体没加载出来,用户看到的只是另一种字体。

字形不同、字宽不同,但每个字都认得出来。

所以拉丁文字站点在这件事上的容错空间很大。

很多性能建议默认了这个前提,才敢让文字先用系统字体显示。

换到非拉丁文字,这个前提要重新验证。

通用的加载策略本身跟语言无关,那部分不在这里展开;这一篇只回答一个问题:把这个策略换到非拉丁文字上,回退时的可读性还能不能得到保证。答案在不同语言里差别很大。

不过拉丁文字也不是完全没有陷阱:如果站点用到了较少见的变音字母,某些系统字体的覆盖也未必完整。中东欧语言的一些字母就属于这一类,回退时会出现个别方框,比整体回退更容易被忽略。

非拉丁文字可能直接回退成方框

如果系统里没有覆盖这套文字的字体,回退就是方框。

方框不是难看的问题,是完全不可读。

用户看到的是一整页整齐排列的空盒子。

这种情况在老设备和某些精简系统上确实会出现。

此时先显示文字反而不如先什么都不显示。

因为一页方框给人的印象是页面坏了,用户会直接离开。

判断自己的市场属于哪种情况,靠的是实测而不是推断。找几台目标市场的主流低端机,把自定义字体禁掉打开页面看一眼,五分钟就能得出结论,而这个结论直接决定了加载策略该怎么选。

方框的出现还有个次生影响:它们的宽度跟真实字形不同,整段文字的排版会跟着变,等字体加载完成之后页面会有一次大幅度的重排,用户正在读的位置直接跑掉,体验比单纯的延迟糟糕得多。

还有一种中间状态更麻烦:系统里有覆盖这套文字的字体,但缺少某些扩展字符。此时页面上大部分正常、个别字是方框,运营看一眼觉得没问题就上线了,实际影响的往往正是那些带专有字母的品类词。

闪烁与遮挡两种策略在这里的取舍

先显示回退字体再换过来,好处是内容立刻可读。

代价是字体换过来的瞬间版面会跳一下。

先不显示等字体到位,好处是没有跳动。

代价是这段时间里用户什么也看不到。

拉丁文字通常选前者,因为回退可读。

非拉丁文字要看回退能不能读,读不了就只能选后者。

还有一层是字宽差异带来的跳动幅度。非拉丁文字的回退字体跟自定义字体在字宽上的差距,往往比拉丁文字大得多,切换时整段文字重新排版,跳动幅度可能是好几行,观感比拉丁文字站上的同类跳动糟糕得多。

还有一条中间路线:给回退字体设置跟自定义字体接近的字宽调整参数,让两者占的空间尽量一致,切换时的跳动就能明显减小。这条路在拉丁文字上很成熟,在非拉丁文字上调起来更费劲但同样有效。

系统自带字体能不能省掉这个文件?

各平台对非拉丁文字的覆盖差别

主流移动系统对常见文字的覆盖已经不错。

阿拉伯语、泰语、天城文这几种基本都有内置字体。

但覆盖到不等于好看,内置字体的设计质量参差不齐。

更麻烦的是同一种文字在不同平台上的内置字体差别很大。

同一个页面在两个平台上的观感可能完全不同。

如果品牌对视觉一致性有要求,就绕不开自带字体文件。

这里可以做一个成本对照:视觉一致性的收益是可感知但难量化的,而字体文件带来的加载代价是可以精确测出来的。把两边摆在一起谈,比单纯讨论要不要用自定义字体容易达成共识得多。

做判断之前值得先问一句品牌方到底在意到什么程度。有些品类的用户根本不会注意字体差别,此时用系统字体是完全合理的选择,把省下的加载时间换成更快的首屏,收益比视觉一致性大得多。

低端设备上的覆盖更差

低价机型为了节省存储,内置字体常被裁减。

裁减的方式通常是删掉不常用的文字系统。

某些区域定制的机型只保留本地语言和英文。

这对本地市场是好事,对跨语言的站点是坏事。

用户设备的语言覆盖跟你的页面语言未必对得上。

这类问题在真机测试里能发现,在模拟器上发现不了。

低端设备还有一个连带影响:字体文件下载完还要解析,解析要占内存和处理时间。几百KB的文件在旗舰机上解析不到十毫秒,在低端机上可能要几十甚至上百毫秒,这段时间同样卡着首屏。

测试机的选择要按目标市场的实际机型分布来,而不是按团队手里现有的设备。这两个分布通常差得很远,用国内常见的中高端机测东南亚或者中东市场,得出的结论基本没有参考价值。

还有一类设备容易被漏掉:车机、电视盒子和部分平板的定制系统,它们的字体覆盖比手机更窄,而这类设备在某些市场的访问占比并不低。抽查时把它们纳入进来,成本只是多借两台机器。

回退链要写几层、按什么顺序

回退链的第一位写自定义字体。

第二位写目标平台上质量最好的那款内置字体。

第三位写另一个平台的对应内置字体。

最后写一个通用的族名兜底。

写四层通常够用,写太多反而增加匹配成本。

每一层都要在真机上验过确实存在。

还有个容易忽略的细节:回退链里的字体如果不支持页面上的某些字符,浏览器会逐个字符去链里找,结果是同一段文字里不同的字用了不同的字体,观感非常混乱。所以回退链里的每一款都应该覆盖完整的字符集。

不同语言版本的回退链应该分别定义,不要写一条通用的塞给所有语言。一条通用回退链要么长得离谱,要么对某些语言完全无效,按语言各写一条虽然多花点功夫但每一条都能真正生效。

首屏上真正被字体拖住的是哪几个元素?

标题、价格、导航三处的先后

标题是首屏上最先被看到的文字,优先级最高。

价格是决定用户去留的关键信息,优先级同样高。

导航文字虽然在顶部,但用户不会立刻去读。

正文在首屏下方,可以最后加载。

按这个顺序安排,能明显改善感知速度。

感知速度跟实际加载时间不是一回事,前者才影响判断。

具体做法是把标题和价格用到的字符单独做一个极小的子集先加载,正文字体延后。这个小子集在拉丁文字上省不了多少,因为本来就小;在非拉丁文字上省下的比例非常可观,这是少数几个语言差异带来正向收益的地方。

判断哪些元素属于首屏,要按目标市场的主流屏幕尺寸算,不要按设计稿的画布尺寸算。这两个数在低端机占比高的市场差别很大,按设计稿判断会把实际上在首屏之外的元素也算成高优先级。

图标字体是另一个被忽略的来源

不少站点用字体的方式来实现图标。

图标字体本身的体积不大,但它是一个额外的请求。

而且它通常在样式表里被引用,会阻塞渲染。

小语种站的字体请求本来就更重,再加一个更雪上加霜。

把图标改成矢量图形内联,能省掉一整个请求。

这一改跟语言无关,但在小语种站上收益更明显。

还有个连带好处:图标字体在回退时会显示成随机的字符,因为那些码位在普通字体里对应的是别的东西。非拉丁文字的页面上出现几个莫名其妙的字母,比拉丁文字页面上更突兀。

如果暂时改不动图标方案,至少可以把图标字体的加载改成不阻塞渲染的方式。图标晚出现几百毫秒对用户几乎没有影响,而文字晚出现同样的时间影响就很大,两者的优先级不该相同。

正文字体可以延后,标题不行

正文在首屏可见区域之外,延后加载没有代价。

标题在首屏正中,延后就是让用户盯着空白。

所以两者应该拆成两个文件分别处理。

拆开之后标题文件可以做得非常小。

因为标题用到的字符是有限且可以预知的。

这个思路在字形集庞大的语言里效果最好。

不过要注意标题字符的可预知程度跟内容类型有关。分类页的标题来自固定的类目名,可以预知;商品详情页的标题来自商品名,几乎不可预知。前者能做极致的小子集,后者只能退回到常用字集。

商品名不可预知这件事还有个折中解法:统计站上全部商品名用到的字符集合,通常会发现它比语言全集小很多,用这个集合做子集既覆盖了所有商品又比全集小得多,代价只是新增商品时要定期重新生成。

格式与压缩能省下多少?

三种格式的体积差

最老的那种格式基本没有压缩,体积最大。

上一代的网页字体格式做了一层压缩,能省三成左右。

新一代格式换了压缩算法,比上一代再省三到四成。

字形数越多,新格式的相对优势越明显。

对小语种站来说,换格式是投入产出比最高的一步。

新一代网页字体格式的规范里给出了压缩方式的细节。

值得注意的是这个格式在2015年还不是所有浏览器都支持,需要同时提供上一代格式作为备选。备选文件不会被支持新格式的浏览器下载,所以不必担心重复传输,代价只是多准备一份文件。

换格式这一步几乎没有副作用,是少数可以放心先做的动作。唯一要注意的是服务端要正确声明文件类型,声明错了浏览器可能拒绝加载,而这个错误在开发环境里往往不会暴露。

另外记得把老格式的文件也做同样的子集处理。有些团队只对新格式做了子集化,备选文件仍是完整版,结果不支持新格式的那批用户下载的反而是最大的那个文件,优化对他们完全没有生效。

压缩对不同文字的效果不一样

压缩算法擅长处理重复的模式。

拉丁字母的轮廓数据里重复模式较多,压缩率高。

汉字的轮廓复杂且相似度低,压缩率明显更低。

阿拉伯语的四形态之间有大量相似结构,压缩率反而不错。

所以不能拿一个统一的压缩比去估算各语言的体积。

各语言各测一次,把真实数字写进文档。

测的时候要用同一款字体的不同语言版本做对比,不要拿不同设计的字体互相比。设计的繁简程度对体积的影响不小,混在一起比会得出误导性的结论,把设计差异算到语言头上。

还有一层是传输时的二次压缩。字体的新格式内部已经压过一轮,服务器再压一次收益很小甚至可能是负的,配置里应该把字体文件排除在通用压缩之外,省下服务器的处理时间。

把各语言的压缩率测出来之后,可以顺手推算一个更有用的数:每增加一门语言,字体这一项要多花多少预算。这个数字拿去做市场扩张的决策讨论非常有说服力,比笼统地说小语种站更慢有效得多。

传输之外还有解析成本

文件下载完之后要解析成内存里的结构。

解析时间跟字形数和排版表的规模都有关系。

字形上万的字体,解析本身就要花掉可观的时间。

这段时间在网络监控里看不到,容易被漏掉。

要量它只能在真机上测渲染时间线。

低端设备上这部分占比会更高。

这也是子集化除了省流量之外的第二个理由:字形少了解析也快。在弱网环境里流量是主要矛盾,在低端设备加良好网络的环境里解析反而成了主要矛盾,两种情况都指向同一个动作。

解析成本还跟字体的内部结构有关。同样的字形数,组织方式不同解析速度可以差出不少,这一点用户无从选择,只能在选型时把候选字体各自在低端机上测一遍实际渲染时间。

分片在小语种上的边界怎么划?

按码位区间分片的原理

分片的做法是把一个大字体切成若干个小文件。

每个小文件声明自己覆盖哪个码位区间。

浏览器只下载页面上实际用到的那几片。

字体模块的规范里定义了声明覆盖范围的描述符。

这个能力在2015年的浏览器里支持程度不一致。

不支持的浏览器会把所有分片都下载下来,反而更慢。

所以在这个阶段用分片要先确认目标市场的浏览器分布。如果主流浏览器的支持率不够,分片带来的收益会被不支持的那部分用户的额外下载抵消掉,整体反而是负的。

分片方案的另一个好处是缓存效率。用户访问站内多个页面时,已经下载过的分片会被复用,只有遇到新字符才下载新片。这个收益在多页面浏览的场景里会随访问深度累积,单看首屏数据体现不出来。

一个词跨两个分片会怎样

分片的边界是按码位划的,不是按语言划的。

一个词里的字符如果分属两个码位区间,就要下载两片。

混排的内容天然会跨片,比如本地文字加拉丁型号。

跨片本身不影响正确性,只是多一个请求。

但如果两片的加载时间差很大,会出现半段文字先显示的现象。

这种半段渲染在视觉上比整段延迟更难看。

可以通过把常一起出现的码位区间合并成一片来缓解。比如把本地文字和基本拉丁字母放在同一片里,代价是这一片变大,但避免了混排内容的跨片问题,对电商站通常是划算的。

半段渲染在从右往左的页面上更难看,因为先出来的那半段位置会随着后半段的到达而整体移动。这类问题跟窄屏上混排内容的折行错位叠加之后,观感会成倍变差。

哪些语言适合分片、哪些不适合

字形集极大的语言最适合分片,比如汉字和日文。

因为任何一个页面用到的字符都只是全集的很小一部分。

字形集中等的语言分片收益有限。

阿拉伯语这类必须保留完整形态表的语言,分片会带来麻烦。

因为形态表是跨字符的,切开之后规则可能失效。

这类语言更适合做一次性的整体子集。

判断依据可以简化成一句话:字符之间彼此独立的语言适合分片,字符之间有渲染依赖的语言不适合。同时使用三套文字的语言属于前者,可以按三套文字分开分片,收益很大。

还有一类需要单独判断的是没有空格分词的文字。这类内容的用字范围通常比预想的分散,因为词与词之间没有边界提示,内容里出现生僻字的概率更高,不用空格分词的语言做分片时要把分片划得宽一些。

分片带来的请求数问题

分片越细,请求数越多。

请求数多了之后,连接建立的开销开始变得可观。

在高延迟的移动网络上,这个开销尤其明显。

所以分片的粒度要在文件大小和请求数之间取平衡。

经验上一个页面触发的字体请求不宜超过三四个。

超过这个数就该考虑合并。

这个平衡点跟目标市场的网络质量直接相关。网络条件好的市场可以分得细一点,网络条件差、延迟高的市场应该合并成更少的大文件,宁可多传一点数据也要少建几次连接。

请求数还要跟页面上其他资源的请求合并起来看。字体请求跟脚本、样式表争抢的是同一批连接,单独优化字体请求数而不看全局,很可能只是把瓶颈从一处挪到了另一处。

怎么量:一套按语言拆开的度量口径

全站平均值会骗人

大部分站点的流量集中在主语言上。

全站的平均加载时间基本反映的是主语言的表现。

小语种版本再慢,也拉不动这个平均值。

所以看全站数字的结论永远是一切正常。

只有把数据按语言拆开,问题才会显形。

拆开之后往往会发现差距大得出乎意料。

这跟按设备拆分的道理一样,只是维度不同。按语言分面做灰度与观测那套方法在这里同样适用,指标换成加载相关的即可。

更隐蔽的是采样偏差。性能数据的采集往往依赖前端上报,而加载失败或者用户提前离开的会话根本不会上报,小语种版本恰恰是这类会话比例更高的,于是数据不但被平均掉,剩下的样本还是偏好的那一批。

按语言分面的四个指标

第一个是字体文件的总字节数。

第二个是字体请求的数量。

第三个是首屏主要文字可读的时间点。

第四个是字体切换造成的版面跳动幅度。

四个指标各语言各测一份,做成一张对照表。

表里差距最大的那一行就是下一步该动的地方。

第四个指标最容易被漏掉,因为它不在常规的性能面板里。测它的办法是录屏之后逐帧看文字位置的变化,虽然笨但可靠,而且这个指标在非拉丁文字上的数值通常远高于拉丁文字。

四个指标之外可以再记一个辅助数字:字体请求的失败率。跨境访问时字体文件来自哪个节点、有没有被中间环节干扰,各市场差别很大,失败率高的市场再怎么优化体积也没用,得先解决可达性。

指标要跟版本一起记录,也就是每次字体文件更新都留一行。字体是那种一旦上线就没人再动的资源,几年后回头看根本说不清中间经历过哪些调整,有了这份记录,排查历史问题时能省下大量猜测。

给每个语言版本单独定预算

统一的性能预算对小语种版本是不公平的。

因为字体这一项的下限本来就不一样。

正确做法是按语言分别定预算,字体那一项各算各的。

预算定得太紧会逼团队做出伤害可读性的取舍。

定得太松则失去了约束作用。

合理的做法是先测出各语言的实际值,再按此设定收紧目标。

预算文档里要写清楚这个数为什么是这个数,把字形数量的差别写进去。否则半年后换了人接手,看到小语种版本的预算比主语言宽松,第一反应会是有人在偷懒,然后又要把整件事重新解释一遍。

预算的执行要落到具体的检查点上,比如构建时自动比对字体文件大小、超过阈值就报警。写在文档里而没有自动检查的预算,半年内一定会失效,这跟任何依赖自觉的流程是一样的下场。

哪些不归语言层,要交出去

通用的性能优化归技术层

图片压缩、脚本拆分、缓存策略这些跟语言无关。

它们该怎么做,换成任何语言的站点都一样。

这一篇只讲字形集这个语言变量带来的额外部分。

把两者混在一起讲,结论会变得含糊。

分开之后,语言层要做的事其实很集中。

就是字形集、子集边界、回退链这三件。

判断某个动作归不归这里,用同一条判据:把语种换成英语还成不成立。图片压缩换成英语照样要做,不归这里;子集化时要不要保留连写规则,换成英语这个问题根本不存在,归这里。

这条判据还能反过来用来排优先级:通用优化的收益在所有语言版本上是共享的,语言层优化的收益只落在单个市场。所以当两类工作抢资源时,先做通用的那一批通常更划算,除非某个小语种市场的问题已经严重到影响可用。

移动端的判定与架构

页面在窄屏上快不快,是通用的技术议题。

它的判定方式与权重变化有专门的讨论。

可以参考移动友好判定的来龙去脉首屏内容的版面机制

多语言站的目录结构与语言地区标注同样是架构问题。

多语言站的架构与标注那套走即可。

本篇涉及架构的只有一处:不同语言版本要能加载各自的字体。

这属于资源分配,跟目录结构无关。

实现上有个常见的疏漏:字体的引用写在全站共用的样式表里,于是每个语言版本都下载了所有语言的字体。检查这一条只需要看一眼网络面板里的请求列表,出现了当前页面用不到的字体就是中了。

还有一个跟架构相关的疏漏是缓存策略。字体文件适合设很长的缓存有效期并用文件名带指纹的方式更新,但不少站点把它跟页面资源用同一套缓存规则,导致用户每隔几天就要重新下载一次几百KB的文件。

字体授权与设计选型

非拉丁文字的字体选择本来就比拉丁文字少得多。

可用的字重、字宽和风格变体都更有限。

商业授权按字形数计价的情况也存在。

这些属于采购与设计的决策,不在这里展开。

需要提醒的只有一点:选型时把体积作为一个硬指标。

设计上稍作让步换来的加载收益,在这些市场往往更值。

选型时还可以关注一下字体项目本身的维护状态。覆盖多种文字的开源字体项目通常会持续补充字符和修正排版表,选一个在维护的项目,意味着以后遇到缺字形的情况还有解决途径,而不是只能自己改。

另外要确认授权是否覆盖网页嵌入这种用法。不少字体的桌面授权不包含网页嵌入,而这两类授权的价格差别不小,采购时如果没说清楚,等到上线后才发现就只能临时换字体,整套子集化和测试都要重做。

常见问题解答

小语种站的字体文件到底比英文站大多少?

按字形数量大致可以排出一个梯度:只支持英文的字重两百到三百个字形、覆盖西欧语言五百上下、阿拉伯字母系统上千、泰文与天城文一千到两千、韩文如果存现成音节有两千多、汉字常用集六千起。每上一档,文件体积大致跟着翻一倍到几倍。但体积的增长不完全来自字形数,还要加上排版规则表,这些表告诉渲染引擎哪个字符该换成哪个形态、记号该放在什么位置,在阿拉伯文字和泰文这类语言里占的比例相当可观,而拉丁文字里基本可以忽略。所以那些流传很广的字体优化经验值背后假设的都是几十KB的拉丁字体,直接搬到小语种站会得出完全错误的判断。正确做法是各语言各测一次,把真实数字写进文档,用同一款字体的不同语言版本做对比,别拿不同设计的字体互相比。数自己那份文件的字形数只要五分钟,任何字体编辑工具打开都能看到,比引用任何外部经验值都可靠。

子集化能不能像英文站那样激进地裁?

不能,非拉丁文字的子集化有三条硬边界。第一条是一个字符可能对应四五个字形,裁字符不等于裁字形,而某个形态会不会被用到取决于这个字符出现在词的什么位置,静态分析算不准,稳妥做法是保留某个字符的全部形态只在字符层面裁剪。第二条是排版规则表不能随便删,工具的判断依据是输入的字符集合而不是真实内容,字符集合里少一个字符可能让整条连写规则被删掉,结果是大部分文字正常、个别词的连写没了,这类错误只有懂这门语言的人才看得出来。第三条是很多语言的数字在跟字母不同的码位区间,只按字母范围取会把数字整组丢掉。防这些错误的实用办法是准备一段专门的测试文本,把已知需要连写的组合和各类数字都塞进去,每次子集化之后渲染这段文本截图比对。在弄清楚具体影响之前,先把工具里保留全部排版规则的开关打开,省下的那点体积远抵不上渲染出错的代价。

让文字先用系统字体显示,这个建议在小语种站成立吗?

要看这门文字在目标设备上有没有可用的回退字体,答案在不同语言里差别很大。拉丁文字的系统覆盖是完整的,字体没加载出来用户看到的只是另一种字体,每个字都认得出来,容错空间很大,那些性能建议默认的就是这个前提。非拉丁文字如果系统里没有覆盖这套文字的字体,回退就是一整页方框,不是难看而是完全不可读,用户的第一反应是页面坏了然后直接离开,这种情况下先什么都不显示反而更好。判断自己的市场属于哪种情况靠实测不靠推断:找几台目标市场的主流低端机,把自定义字体禁掉打开页面看一眼,五分钟就能得出结论。还要额外考虑字宽差异,非拉丁文字回退字体与自定义字体的字宽差距往往更大,切换时整段重排的跳动幅度可能是好几行。还有一条中间路线是给回退字体设置接近的字宽调整参数,让两者占的空间尽量一致,切换时的跳动能明显减小。

首屏字体该怎么拆才能既快又不难看?

把标题和价格用到的字符单独做一个极小的子集优先加载,正文字体延后。理由是标题在首屏正中,延后就是让用户盯着空白;正文在可见区域之外,延后没有代价。这个拆分在拉丁文字上省不了多少因为本来就小,在非拉丁文字上省下的比例非常可观,是少数几个语言差异带来正向收益的地方。要注意标题字符的可预知程度跟内容类型有关:分类页的标题来自固定的类目名可以完全预知,能做极致的小子集;商品详情页的标题来自商品名几乎不可预知,只能退回到常用字集。另外别忘了图标字体,它体积不大但是一个额外的阻塞请求,改成矢量图形内联能省掉一整个请求,而且避免了回退时显示成莫名其妙字符的问题。商品名的字符集合通常比语言全集小很多,用这个集合做子集既覆盖了所有商品又比全集小,代价只是新增商品时要定期重新生成。

按码位分片这个做法值不值得用?

要看语言和浏览器分布两个条件。语言这边的判据可以简化成一句话:字符之间彼此独立的语言适合分片,字符之间有渲染依赖的语言不适合。汉字、日文这类字形集极大且字符独立的最适合,任何一个页面用到的都只是全集的很小一部分;阿拉伯语这类必须保留完整形态表的不适合,切开之后跨字符的规则可能失效。浏览器这边要确认目标市场的支持率,不支持的浏览器会把所有分片都下载下来反而更慢,收益会被抵消。还有两个实操细节:混排内容天然跨片,把本地文字和基本拉丁字母合并到同一片能避免半段渲染;分片粒度要看网络质量,高延迟市场应该合并成更少的大文件,宁可多传数据也要少建连接,一个页面的字体请求不宜超过三四个。分片的另一个收益在缓存上,用户访问站内多个页面时已下载的分片会被复用,这部分好处单看首屏数据体现不出来。

为什么全站的性能数据看起来都正常?

因为流量集中在主语言上,全站平均值基本反映的是主语言的表现,小语种版本再慢也拉不动这个平均值,所以看全站数字的结论永远是一切正常。必须按语言把数据拆开,拆开之后往往会发现差距大得出乎意料。建议固定看四个分语言指标:字体文件的总字节数、字体请求的数量、首屏主要文字可读的时间点、字体切换造成的版面跳动幅度。第四个最容易被漏掉因为它不在常规的性能面板里,测它要录屏之后逐帧看文字位置的变化,虽然笨但可靠,而且这个数值在非拉丁文字上通常远高于拉丁文字。四个指标做成一张按语言排的对照表,差距最大的那一行就是下一步该动的地方。性能预算也要按语言分别定,字体那一项各算各的,并在文档里写清楚这个数为什么是这个数。还要留意采样偏差,加载失败或提前离开的会话根本不会上报,而小语种版本恰恰是这类会话比例更高的那一批。

预算有限时,这几件事该按什么顺序做?

第一步换格式,这是投入产出比最高的一步,改一次配置就能省掉三到四成,字形数越多相对优势越明显,同时保留上一代格式作为备选,不支持新格式的浏览器才会去下载它。第二步做保守的子集化,只在字符层面裁剪、保留全部形态和排版表,配上那段测试文本做验证。第三步拆分首屏字体,把标题和价格的小子集单独提出来优先加载。第四步整理回退链,四层结构写清楚并在真机上逐层验过存在。第五步才是考虑分片,因为它的收益依赖语言特性和浏览器支持率,前面四步在任何语言上都稳定有效。整个过程中把按语言分面的四个指标测下来,每做完一步更新一次,这样每一步的收益都能单独看到,而不是做完一大堆改动之后说不清是哪一项起了作用。每一步做完都把四个分语言指标更新一次,这样收益能逐步归因,而不是做完一大堆改动之后说不清是哪一项起了作用。

权威参考资料

分享到
标签
版权声明

本文标题:《网页字体在英文站只占几十KB,到了小语种站就变成拖慢首屏的最大一块》

本文链接:https://zhangwenbao.com/minor-language-web-font-glyph-set-first-screen-cost.html

版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交