网页字体在英文站只占几十KB,到了小语种站就变成拖慢首屏的最大一块
本文目录
- 同一个字体文件,换一种语言为什么会重好几倍?
- 一份拉丁字体里到底有多少个字形
- 换成非拉丁文字之后这个数怎么涨
- 体积不是线性关系,还要加上排版表
- 非拉丁文字的字形集大在哪几处?
- 一个字母四种呈现形态
- 连字与必需连写
- 上下叠加的记号需要定位表
- 合体字与音节组合
- 子集化在小语种上为什么不能照搬拉丁语的做法?
- 拉丁语可以按字符频率裁,非拉丁不行
- 删掉排版表会渲染错
- 数字属于另一个码位区间,最容易漏
- 组合记号被裁掉的后果
- 字体不加载的后果,在两类文字里不是一回事
- 拉丁文字回退到系统字体仍然可读
- 非拉丁文字可能直接回退成方框
- 闪烁与遮挡两种策略在这里的取舍
- 系统自带字体能不能省掉这个文件?
- 各平台对非拉丁文字的覆盖差别
- 低端设备上的覆盖更差
- 回退链要写几层、按什么顺序
- 首屏上真正被字体拖住的是哪几个元素?
- 标题、价格、导航三处的先后
- 图标字体是另一个被忽略的来源
- 正文字体可以延后,标题不行
- 格式与压缩能省下多少?
- 三种格式的体积差
- 压缩对不同文字的效果不一样
- 传输之外还有解析成本
- 分片在小语种上的边界怎么划?
- 按码位区间分片的原理
- 一个词跨两个分片会怎样
- 哪些语言适合分片、哪些不适合
- 分片带来的请求数问题
- 怎么量:一套按语言拆开的度量口径
- 全站平均值会骗人
- 按语言分面的四个指标
- 给每个语言版本单独定预算
- 哪些不归语言层,要交出去
- 通用的性能优化归技术层
- 移动端的判定与架构
- 字体授权与设计选型
- 常见问题解答
- 小语种站的字体文件到底比英文站大多少?
- 子集化能不能像英文站那样激进地裁?
- 让文字先用系统字体显示,这个建议在小语种站成立吗?
- 首屏字体该怎么拆才能既快又不难看?
- 按码位分片这个做法值不值得用?
- 为什么全站的性能数据看起来都正常?
- 预算有限时,这几件事该按什么顺序做?
- 权威参考资料
摘要:字体文件的体积不是一个固定值,它跟着语言走。一份拉丁字体装的是几百个字形,换成阿拉伯语要装上千个,换成印度诸语要装几千个,换成汉字要装上万个。字形数量之外还有排版表,连写、叠加记号、合体字各自需要一套规则数据。于是同一个设计、同一种格式,做英文站几十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