🪒Web端字体切割压缩方案

2,437 阅读3分钟

今天接到一个系统需求,需要将系统内的文字替换为鸿蒙OS的字体(developer.huawei.com/consumer/cn…),但是鸿蒙OS的字体单Regular的文件就有8M+。这个直接放到网页上,会极大影响使用体验。

于是乎,开始考虑怎么开始对字体文件进行压缩。

第一种方案

一般来说,字体格式体积由大到小排列(性能从差到好):

ttf,eot(IE8),woff,woff2

从官网下载的鸿蒙字体是ttf格式的,尝试用了woff2格式的(github.com/IKKI2000/ha…),但是还是有4M多。对于Web端来说,还是太大了。

第二种方案

把系统内未使用过的汉字裁掉,只保留系统内使用的汉字,这样就可以大大减少字体文件体积。但是呢这种方式只适用于固定内容的网页,像一些动态内容(服务端返回的汉字,评论等)就没法适应。

这种方式可以使用 Font-spider-Plus 组件进行压缩。具体方法可以看到组件文档。

第三种方案

上面两种方式,对于我来说都不太理想,于是只能继续寻找方案。很好奇,鸿蒙的官网也是用的鸿蒙的字体,那它是怎么兼顾性能的呢?

通过Devtool能看到,官网加载的字体文件非常多,但是每个只有几十kb。像是被切割成了N份。

于是翻看对应的css文件,发现了这一句注释

/** generated by https://github.com/voderl/font-slice */

进入github仓库一看,发现了今天的主角 font-slice。(组件Demo地址

效果

得意黑字体为例为例:

处理前 ttf 大小 2074KB,woff2 大小 928KB.

处理后每个类型的字体生成 95 个文件:

ttf 总大小为 2.3M (最小文件 3.4K,最大文件 55K)

woff2 总大小为 1.3M (最小文件 1.5K,最大文件 33K)

实际加载页面的体积由页面使用的字符决定,以该页面为例,只需要加载 386KB 就能覆盖全部字符。

原理

将中文字体按照 Google Fonts 的切割子集方案,生成多个较小体积的资源包。仅需加载小部分字体资源即可展示完整页面。

即它采用了机器学习等手段,将字体拆分成合适的粒度,比如把一个 4MB 的字体包分成 100 个 40KB 的字体包,这样的话,一般网页中使用到的中文也只是一部分字体,只需要加载多个资源包就能完全覆盖。同时,就算网页中有很多生僻字,需要付出的代价也只是多加载几个资源包。

使用

1. 安装

npm install --save-dev font-slice

2.创建脚本

const path = require("path");
const createFontSlice = require('font-slice');

createFontSlice({
  // fontPath
  fontPath: path.resolve(__dirname, 'YourPath.ttf'),
  // outputDir
  outputDir: path.resolve(__dirname, './output'),
  fontFamily: 'HarmonyOS_SansSC',
  // 是否需要在生成完成后打开预览页面,默认为 true,如果为 false 不会生成 index.html 及启动服务器
  preview: false,
})

这一步所有的配置项可以查看:github.com/voderl/font…

3. 配置script

我是把这个脚本配置到了package.json的scripts里面,后续就可以通过npm run来运行了。这一步可自行决定。

4.引入CSS

运行完成后,会输出font.css和切割成多份的字体文件。将css引入页面。页面内的地方就可以正常使用font-family了。

缺点

这种方式有个缺点,就是如果新出现的文字没有加载对应的分片,会有一瞬间的文字闪动。因为需要加载新的文字分片。

参考文档

  1. web性能-字体优化-掘金
  2. font-slice组件
  3. 字蛛+(Font-spider-plus)
  4. 鸿蒙字体下载