“
阅读本文大概需要 3 分钟。
”在网页开发的过程中,经常我们会需要定制一些特殊的中文字体,为了呈现某个中文字体,那么网页里面就必须引入一个中文字体文件,这时候就引入了这样的麻烦:
•中文字不像英文一样,英文字体一般比中文字体小得多,因为英文字体通常只需要做 26 个英文字母的就够了,而中文汉字多啊,每个字都得做一个字体,所以最终中文字体文件就大多了。随意最终下来,英文字体一般都超过 1M,中文字体都 10M 以上了。•在一些场景下,我们可能用不了那么多字体,比如我们可能其实就用的常见的 1000 多个字,远少于字体包里面的汉字数量。引入了但是不用,那不就浪费了吗?白白浪费流量。
所以,为了避免空间的浪费,我们需要对字体进行压缩,比如选取常用的一些字来压缩,或者在个别情况下只对其中的几个字来进行压缩。
压缩字体的工具也不少,开源的有 font-spider、fontmin、fontmin-app 等,这里就简单记录说明下他们的用法。
fontmin
fontmin 是一个纯 JavaScript 字体子集化方案,利用 fontmin 我们可以轻松提取 ttf 字体文件中需要用到的字,然后输出,另外它还支持转为 eot、woff、sbg 等格式。
这个 fontmin 是基于 Node.js 来运行的,所以这里我们在用的时候需要先安装好 Node.js,并用 npm 初始化一个仓库。
执行这么一条命令初始化 Project:
npm init
进入 Project,然后安装 fontmin:
npm install --save fontmin
运行完了我们就可以来使用 fontmin 了,首先这里我们准备几个字体文件,比如 NotoSansCJKsc-Medium.ttf 字体文件,这个字体算是非常大的了,一个文件 17M,如果我们把这个放到网站里直接引入这还了得。
所以下面我们就可以来对这字体进行压缩了,提取出我们想要的字体然后重新输出只包含特定文字的字体,这样字体就会变得很小了。
这里介绍一个最常用的操作,代码如下:
var Fontmin = require('fontmin')var fontmin = new Fontmin()// 字体文件路径.src('source/*.ttf')// 指定要提取的汉字.use(Fontmin.glyph({text: '赵钱孙李周吴郑王',}))// 转 woff 格式并压缩.use(Fontmin.ttf2woff({deflate: true // 压缩}))// 输出.dest('build/')// 运行
fontmin.run(function (err, files) {if (err) throw err
})
逻辑上很简单吧,我们把待处理的字体文件放到 source 目录下,然后利用 Fontmin 的 glyph 来指定提取哪些汉字,这里我们就指定了八个汉字「赵钱孙李周吴郑王」,然后再转 woff 格式并压缩,最后把结果输出到 build 目录。
我们把代码保存为 compress.js,然后运行:
node comporess.js
看下输出结果:
可以看到,整个字体就输出到 build 目录了,woff 格式是 4KB,ttf 格式的 6KB。
那这输出的字体文件到底是好不好用的呢?我们来写个网页验证下。
新建一个 html 文件,内容如下:
<!DOCTYPE html>
<html lang="en">
<head><style>@font-face {font-family: "compressed";src: url("./build/NotoSansCJKsc-Regular.woff");}p, h1 {font-family: "compressed", "Songti SC", sans-serif;text-align: center;font-size: 40px;}h1 {font-size: 60px;}</style>
</head>
<body>
<h1>百家姓</h1>
<p>赵钱孙李,周吴郑王。</p>
<p>冯陈褚卫,蒋沈韩杨。</p>
<p>朱秦尤许,何吕施张。</p>
<p>孔曹严华,金魏陶姜。</p>
</body>
</html>
这里我们定义了一个 font-face,font-family 指定名称叫做 compressed,字体文件引入的就是一个输出的 woff 文件,然后指定了 p 节点的样式首选字体为 compressed,第二字体为宋体,这两个字体差别比较大,所以如果某个字没有的话就会显示宋体,这样我们就知道这个字体生没生效了。
好,接下来我们看下效果:
这里我们可以看到,「赵钱孙李周吴郑王」八个字是显示了对应字体的样式,其他的字由于本身我们没有提取出来,所以字体文件里找不到,它就显示了宋体。
那由此我们就可以确定字体已经压缩成功啦。
不过这里我们只压缩了八个字,一般来说我们要把常用的一些汉字放进来,这里我搜了常用的 3600 多个中文字加标点:
。?!,、;:“”‘’…()《》〈〉【】.,:;!?-'_"'()[]<>|&~;+-*/=<>0123456789ABCEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz一乙二十丁厂七卜人入八九几儿了力乃刀又三于干亏士工土才寸下大丈与万上小口巾山千乞川亿个勺久凡及夕丸么广亡门义之尸弓己已子卫也女飞刃习叉马乡丰王井开夫天无元专云扎艺木五支厅不太犬区历尤友匹车巨牙屯比互切瓦止少日中冈贝内水见午牛手毛气升长仁什片仆化仇币仍仅斤爪反介父从今凶分乏公仓月氏勿欠风丹匀乌凤勾文六方火为斗忆订计户认心尺引丑巴孔队办以允予劝双书幻玉刊示末未击打巧正扑扒功扔去甘世古节本术可丙左厉右石布龙平灭轧东卡北占业旧帅归且旦目叶甲申叮电号田由史只央兄叼叫另叨叹四生失禾丘付仗代仙们仪白仔他斥瓜乎丛令用甩印乐句匆册犯外处冬鸟务包饥主市立闪兰半汁汇头汉宁穴它讨写让礼训必议讯记永司尼民出辽奶奴加召皮边发孕圣对台矛纠母幼丝式刑动扛寺吉扣考托老执巩圾扩扫地扬场耳共芒亚芝朽朴机权过臣再协西压厌在有百存而页匠夸夺灰达列死成夹轨邪划迈毕至此贞师尘尖劣光当早吐吓虫曲团同吊吃因吸吗屿帆岁回岂刚则肉网年朱先丢舌竹迁乔伟传乒乓休伍伏优伐延件任伤价份华仰仿伙伪自血向似后行舟全会杀合兆企众爷伞创肌朵杂危旬旨负各名多争色壮冲冰庄庆亦刘齐交次衣产决充妄闭问闯羊并关米灯州汗污江池汤忙兴宇守宅字安讲军许论农讽设访寻那迅尽导异孙阵阳收阶阴防奸如妇好她妈戏羽观欢买红纤级约纪驰巡寿弄麦形进戒吞远违运扶抚坛技坏扰拒找批扯址走抄坝贡攻赤折抓扮抢孝均抛投坟抗坑坊抖护壳志扭块声把报却劫芽花芹芬苍芳严芦劳克苏杆杠杜材村杏极李杨求更束豆两丽医辰励否还歼来连步坚旱盯呈时吴助县里呆园旷围呀吨足邮男困吵串员听吩吹呜吧吼别岗帐财针钉告我乱利秃秀私每兵估体何但伸作伯伶佣低你住位伴身皂佛近彻役返余希坐谷妥含邻岔肝肚肠龟免狂犹角删条卵岛迎饭饮系言冻状亩况床库疗应冷这序辛弃冶忘闲间闷判灶灿弟汪沙汽沃泛沟没沈沉怀忧快完宋宏牢究穷灾良证启评补初社识诉诊词译君灵即层尿尾迟局改张忌际陆阿陈阻附妙妖妨努忍劲鸡驱纯纱纳纲驳纵纷纸纹纺驴纽奉玩环武青责现表规抹拢拔拣担坦押抽拐拖拍者顶拆拥抵拘势抱垃拉拦拌幸招坡披拨择抬其取苦若茂苹苗英范直茄茎茅林枝杯柜析板松枪构杰述枕丧褚或画卧事刺枣雨卖矿码厕奔奇奋态欧垄妻轰顷转斩轮软到非叔肯齿些虎虏肾贤尚旺具果味昆国昌畅明易昂典固忠咐呼鸣咏呢岸岩帖罗帜岭凯败贩购图钓制知垂牧物乖刮秆和季委佳侍供使例版侄侦侧凭侨佩货依的迫质欣征往爬彼径所舍金命斧爸采受乳贪念贫肤肺肢肿胀朋股肥服胁周昏鱼兔狐忽狗备饰饱饲变京享店夜庙府底剂郊废净盲放刻育闸闹郑券卷单炒炊炕炎炉沫浅法泄河沾泪油泊沿泡注泻泳泥沸波泼泽治怖性怕怜怪学宝宗定宜审宙官空帘实试郎诗肩房诚衬衫视话诞询该详建肃录隶居届刷屈弦承孟孤陕降限妹姑姐姓始驾参艰线练组细驶织终驻驼绍经贯奏春帮珍玻毒型挂封持项垮挎城挠政赴赵挡挺括拴拾挑指垫挣挤拼挖按挥挪某甚革荐巷带草茧茶荒茫荡荣故胡南药标枯柄栋相查柏柳柱柿栏树要咸威歪研砖厘厚砌砍面耐耍牵残殃轻鸦皆背战点临览竖省削尝是盼眨哄显哑冒映星昨畏趴胃贵界虹虾蚁思蚂虽品咽骂哗咱响哈咬咳哪炭峡罚贱贴骨钞钟钢钥钩卸缸拜看矩怎牲选适秒香种秋科重复竿段便俩贷顺修保促侮俭俗俘信皇泉鬼侵追俊盾待律很须叙剑逃食盆胆胜胞胖脉勉狭狮独狡狱狠贸怨急饶蚀饺饼弯将奖哀亭亮度迹庭疮疯疫疤姿亲音帝施闻阀阁差养美姜叛送类迷前首逆总炼炸炮烂剃洁洪洒浇浊洞测洗活派洽染济洋洲浑浓津恒恢恰恼恨举觉宣室宫宪突穿窃客冠语扁袄祖神祝误诱说诵垦退既屋昼费陡眉孩除险院娃姥姨姻娇怒架贺盈勇怠柔垒绑绒结绕骄绘给络骆绝绞统耕耗艳泰珠班素蚕顽盏匪捞栽捕振载赶起盐捎捏埋捉捆捐损都哲逝捡换挽热恐壶挨耻耽恭莲莫荷获晋恶真框桂档桐株桥桃格校核样根索哥速逗栗配翅辱唇夏础破原套逐烈殊顾轿较顿毙致柴桌虑监紧党晒眠晓鸭晃晌晕蚊哨哭恩唤啊唉罢峰圆贼贿钱钳钻铁铃铅缺氧特牺造乘敌秤租积秧秩称秘透笔笑笋债借值倚倾倒倘俱倡候俯倍倦健臭射躬息徒徐舰舱般航途拿爹爱颂翁脆脂胸胳脏胶脑狸狼逢留皱饿恋桨浆衰高席准座脊症病疾疼疲效离唐资凉站剖竞部旁旅畜阅羞瓶拳粉料益兼烤烘烦烧烛烟递涛浙涝酒涉消浩海涂浴浮流润浪浸涨烫涌悟悄悔悦害宽家宵宴宾窄容宰案请朗诸读扇袜袖袍被祥课谁调冤谅谈谊剥恳展剧屑弱陵陶陷陪娱娘通能难预桑绢绣验继球理捧堵描域掩捷排掉堆推掀授教掏掠培接控探据掘职基著勒黄萌萝菌菜萄菊萍菠营械梦梢梅检梳梯桶救副票戚爽聋袭盛雪辅辆虚雀堂常匙晨睁眯眼悬野啦晚啄距跃略蛇累唱患唯崖崭崇圈铜铲银甜梨犁移笨笼笛符第敏做袋悠偿偶偷您售停偏假得衔盘船斜盒鸽悉欲彩领脚脖脸脱象够猜猪猎猫猛馅馆凑减毫麻痒痕廊康庸鹿盗章竟商族旋望率着盖粘粗粒断剪兽清添淋淹渠渐混渔淘液淡深婆梁渗情惜惭悼惧惕惊惨惯寇寄宿窑密谋谎祸谜逮敢屠弹随蛋隆隐婚婶颈绩绪续骑绳维绵绸绿琴斑替款堪搭塔越趁趋超提堤博揭喜插揪搜煮援裁搁搂搅握揉斯期欺联散惹葬葛董葡敬葱落朝辜葵棒棋植森椅椒棵棍棉棚棕惠惑逼厨厦硬确雁殖裂雄暂雅辈悲紫辉敞赏掌晴暑最量喷晶喇遇喊景践跌跑遗蛙蛛蜓喝喂喘喉幅帽赌赔黑铸铺链销锁锄锅锈锋锐短智毯鹅剩稍程稀税筐等筑策筛筒答筋筝傲傅牌堡集焦傍储奥街惩御循艇舒番释禽腊脾腔鲁猾猴然馋装蛮就痛童阔善羡普粪尊道曾焰港湖渣湿温渴滑湾渡游滋溉愤慌惰愧愉慨割寒富窜窝窗遍裕裤裙谢谣谦属屡强粥疏隔隙絮嫂登缎缓编骗缘瑞魂肆摄摸填搏塌鼓摆携搬摇搞塘摊蒜勤鹊蓝墓幕蓬蓄蒙蒸献禁楚想槐榆楼概赖酬感碍碑碎碰碗碌雷零雾雹输督龄鉴睛睡睬鄙愚暖盟歇暗照跨跳跪路跟遣蛾蜂嗓置罪罩错锡锣锤锦键锯矮辞稠愁筹签简毁舅鼠催傻像躲微愈遥腰腥腹腾腿触解酱痰廉新韵意粮数煎塑慈煤煌满漠源滤滥滔溪溜滚滨粱滩慎誉塞谨福群殿辟障嫌嫁叠缝缠静碧璃墙撇嘉摧截誓境摘摔聚蔽慕暮蔑模榴榜榨歌遭酷酿酸磁愿需弊裳颗嗽蜻蜡蝇蜘赚锹锻舞稳算箩管僚鼻魄貌膜膊膀鲜疑馒裹敲豪膏遮腐瘦辣竭端旗精歉熄熔漆漂漫滴演漏慢寨赛察蜜谱嫩翠熊凳骡缩慧撕撒趣趟撑播撞撤增聪鞋蕉蔬横槽樱橡飘醋醉震霉瞒题暴瞎影踢踏踩踪蝶蝴嘱墨镇靠稻黎稿稼箱箭篇僵躺僻德艘膝膛熟摩颜毅糊遵潜潮懂额慰劈操燕薯薪薄颠橘整融醒餐嘴蹄器赠默镜赞篮邀衡膨雕磨凝辨辩糖糕燃澡激懒壁避缴戴擦鞠藏霜霞瞧蹈螺穗繁辫赢糟糠燥臂翼骤鞭覆蹦镰翻鹰警攀蹲颤瓣爆疆壤耀躁嚼嚷籍魔灌蠢霸露囊罐匕刁丐歹戈夭仑讥冗邓艾夯凸卢叭叽皿凹囚矢乍尔冯玄邦迂邢芋芍吏夷吁吕吆屹廷迄臼仲伦伊肋旭匈凫妆亥汛讳讶讹讼诀弛阱驮驯纫玖玛韧抠扼汞扳抡坎坞抑拟抒芙芜苇芥芯芭杖杉巫杈甫匣轩卤肖吱吠呕呐吟呛吻吭邑囤吮岖牡佑佃伺囱肛肘甸狈鸠彤灸刨庇吝庐闰兑灼沐沛汰沥沦汹沧沪忱诅诈罕屁坠妓姊妒纬玫卦坷坯拓坪坤拄拧拂拙拇拗茉昔苛苫苟苞茁苔枉枢枚枫杭郁矾奈奄殴歧卓昙哎咕呵咙呻咒咆咖帕账贬贮氛秉岳侠侥侣侈卑刽刹肴觅忿瓮肮肪狞庞疟疙疚卒氓炬沽沮泣泞泌沼怔怯宠宛衩祈诡帚屉弧弥陋陌函姆虱叁绅驹绊绎契贰玷玲珊拭拷拱挟垢垛拯荆茸茬荚茵茴荞荠荤荧荔栈柑栅柠枷勃柬砂泵砚鸥轴韭虐昧盹咧昵昭盅勋哆咪哟幽钙钝钠钦钧钮毡氢秕俏俄俐侯徊衍胚胧胎狰饵峦奕咨飒闺闽籽娄烁炫洼柒涎洛恃恍恬恤宦诫诬祠诲屏屎逊陨姚娜蚤骇耘耙秦匿埂捂捍袁捌挫挚捣捅埃耿聂荸莽莱莉莹莺梆栖桦栓桅桩贾酌砸砰砾殉逞哮唠哺剔蚌蚜畔蚣蚪蚓哩圃鸯唁哼唆峭唧峻赂赃钾铆氨秫笆俺赁倔殷耸舀豺豹颁胯胰脐脓逛卿鸵鸳馁凌凄衷郭斋疹紊瓷羔烙浦涡涣涤涧涕涩悍悯窍诺诽袒谆祟恕娩骏琐麸琉琅措捺捶赦埠捻掐掂掖掷掸掺勘聊娶菱菲萎菩萤乾萧萨菇彬梗梧梭曹酝酗厢硅硕奢盔匾颅彪眶晤曼晦冕啡畦趾啃蛆蚯蛉蛀唬唾啤啥啸崎逻崔崩婴赊铐铛铝铡铣铭矫秸秽笙笤偎傀躯兜衅徘徙舶舷舵敛翎脯逸凰猖祭烹庶庵痊阎阐眷焊焕鸿涯淑淌淮淆渊淫淳淤淀涮涵惦悴惋寂窒谍谐裆袱祷谒谓谚尉堕隅婉颇绰绷综绽缀巢琳琢琼揍堰揩揽揖彭揣搀搓壹搔葫募蒋蒂韩棱椰焚椎棺榔椭粟棘酣酥硝硫颊雳翘凿棠晰鼎喳遏晾畴跋跛蛔蜒蛤鹃喻啼喧嵌赋赎赐锉锌甥掰氮氯黍筏牍粤逾腌腋腕猩惫敦痘痢痪竣翔奠遂焙滞湘渤渺溃溅湃愕惶寓窖窘雇谤犀隘媒媚婿缅缆缔缕骚瑟鹉瑰搪聘斟靴靶蓖蒿蒲蓉楔椿楷榄楞楣酪碘硼碉辐辑频睹睦瞄嗜嗦暇畸跷跺蜈蜗蜕蛹嗅嗡嗤署蜀幌锚锥锨锭锰稚颓筷魁衙腻腮腺鹏肄猿颖煞雏馍馏禀痹廓痴靖誊漓溢溯溶滓溺寞窥窟寝褂裸谬媳嫉缚缤剿赘熬赫蔫摹蔓蔗蔼熙蔚兢榛榕酵碟碴碱碳辕辖雌墅嘁踊蝉嘀幔镀舔熏箍箕箫舆僧孵瘩瘟彰粹漱漩漾慷寡寥谭褐褪隧嫡缨撵撩撮撬擒墩撰鞍蕊蕴樊樟橄敷豌醇磕磅碾憋嘶嘲嘹蝠蝎蝌蝗蝙嘿幢镊镐稽篓膘鲤鲫褒瘪瘤瘫凛澎潭潦澳潘澈澜澄憔懊憎翩褥谴鹤憨履嬉豫缭撼擂擅蕾薛薇擎翰噩橱橙瓢蟥霍霎辙冀踱蹂蟆螃螟噪鹦黔穆篡篷篙篱儒膳鲸瘾瘸糙燎濒憾懈窿缰壕藐檬檐檩檀礁磷瞬瞳瞪曙蹋蟋蟀嚎赡镣魏簇儡徽爵朦臊鳄糜癌懦豁臀藕藤瞻嚣鳍癞瀑襟璧戳攒孽蘑藻鳖蹭蹬簸簿蟹靡癣羹鬓攘蠕巍鳞糯譬霹躏髓蘸镶瓤矗
我们把这些字提取出来然后压缩下,存成一个 text.txt 文件,然后我们把代码改写如下:
var Fontmin = require('fontmin')
var fs = require('fs');
const string = fs.readFileSync('text.txt', 'utf8').toString()var fontmin = new Fontmin()// 字体文件路径.src('source/*.ttf')// 指定文字.use(Fontmin.glyph({text: string,hinting: false}))// 压缩.use(Fontmin.ttf2woff({deflate: false // deflate woff. default = false}))// 输出.dest('build/')fontmin.run(function (err, files) {if (err) throw err
})
这里我们借助于了一个 fs 库,需要额外安装:
npm install --save fs
重新运行,可以看到字体文件就变成了 800 多 KB 了。
然后我们重新运行下测试网页,效果如下:
现在字体就都正常啦。
现在我们就成功把一些字体都压缩进新的字体文件了,同时字体文件还被压缩的更小了。
fontmin-app
上面的一些操作我们需要用代码来实现,还得依赖于 Node.js 环境,不是很方便,这里再介绍一个基于 fontmin 开发的一个工具,带有 UI 界面,同时还有预览效果,叫做 fontmin-app。
其 GitHub 链接为:https://github.com/ecomfe/fontmin-app。
虽然这个已经很久没人维护了,但是整体还是挺好用的。
大家可以直接到 GitHub 下载对应的客户端,链接为:https://github.com/ecomfe/fontmin-app/releases。
下载打开之后,指定字体文件和要提取的文本,他就可以帮我们输出各种格式的字体了,界面如图所示:
注意这里输入的字体文件必须是 ttf 格式。
最后生成的字体文件如下:
可以看到输出了压缩提取后的字体啦,然后接着用就好了。
font-spider
font-spider,中文叫做字蛛。其介绍如下:
字蛛是一个中文字体压缩器,让网页自由引入中文字体成为可能。字蛛通过分析本地 CSS 与 HTML 文件获取 WebFont 中没有使用的字符,并将这些字符数据从字体中删除以实现压缩,同时生成跨浏览器使用的格式。
第一步就是安装 font-spider,命令如下:
npm install font-spider -g
安装完成之后我们就可以使用 font-spider 命令了。
第二步便是在网页里面正常写,调用正常的字体,index.html 内容类似如下:
<!DOCTYPE html>
<html lang="en">
<head><style>@font-face {font-family: "compressed";src: url("./source/NotoSansCJKsc-Bold.ttf");}p, h1 {font-family: "compressed", sans-serif;text-align: center;font-size: 40px;}h1 {font-size: 60px;}</style>
</head>
<body>
<h1>百家姓</h1>
<p>赵钱孙李,周吴郑王。</p>
<p>冯陈褚卫,蒋沈韩杨。</p>
<p>朱秦尤许,何吕施张。</p>
<p>孔曹严华,金魏陶姜。</p>
</body>
</html>
这里我们指定的字体文件的路径为 source,即源字体文件,17MB 的那个。
下面我们来用 font-spider 压缩下:
font-spider index.html
输出结果如下:
Font family: compressed
Original size: 16989.188 KB
Include chars: 。严何冯华卫吕吴周姓姜孔孙家尤张施曹朱李杨沈王百秦蒋褚许赵郑金钱陈陶韩魏,
Chars length: 37
Font id: 77768b96afca0ea58d24dc33268dd926
CSS selectors: p, h1
Font files:
File source/NotoSansCJKsc-Bold.ttf created: 12.224 KB
我们再看下字体文件的变化:
可以看到它把原来的字体替换成了压缩后的字体,同时在同级目录生成了一个 .font-spider
的文件夹备份了原来的字体文件。
这样在网页里面定义的字体就都被压缩了,相当于用哪些字压缩哪些字。
但是这样就会引入一个问题,现在很多网站都是 Ajax 加载数据了,文字是没法从 HTML 里面读取的,这个就没办法了。
解决方案
所以总上来说吧,使用 font-spider 局限性还是蛮高的,我个人比较倾向于使用 fontmin-app 把字体压缩一下,留下常用的字。
然后不太常用的字我们使用一些备选字体,比如说:
font-family: Note Sans CJK SC, "Microsoft Yahei", sans-serif;
另外还可以通过 font-display 这个选项来控制一些加载节奏,以防止在字体没加载出来的时候不显示文字的现象,具体用法可以参考:https://juejin.im/entry/5984938d6fb9a03c3d024060。
代码
本节代码可以在公众号「崔庆才丨静觅」或扫描下方二维码,回复「FontCompress」获取。
崔庆才丨静觅
隐形字
同名公众号「崔庆才丨静觅」
在这里分享自己的一些经验、想法和见解。
长按识别二维码关注
好文和朋友一起看~