实战日志 小程序字体一致性 6 分钟

微信小程序字体一致性:别全局换字,关键位用子集字体

微信小程序跨开发工具和安卓真机的字体差异,不该用全站自定义字体硬压,而应按正文、关键视觉位和运营素材分层处理。

背景

微信小程序里的字体一致性,经常会被误判成一个 CSS 字体栈问题:只要把 font-family 写完整,Windows 微信开发者工具、Android 微信、iOS 微信就应该看起来一样。

实际不是这样。开发者工具和真机会落到不同系统字体、不同 WebView 或渲染内核,中文笔画粗细、字面宽度、抗锯齿和行高观感都会有细微差异。尤其是价格、日期大数字、按钮和标题,这些位置的差异最容易被设计验收看出来。

所以这个问题不能只问“有没有一个字体栈能 100% 一致”,更该问“哪些文字真的需要一致,哪些文字应该交给平台原生体验”。

这次解决了什么

这次结论很明确:不要把小程序全站正文都切到自定义中文字体。更稳的方案是“正文系统字体 + 关键视觉位自定义字体 + 极少数图片化”。

正文、列表、表单、说明文案继续使用系统字体,让布局容忍 1-3px 的字宽和粗细差异。关键视觉位,比如价格数字、日期大字、活动标题、Tab、按钮短文案,可以加载同一份子集化字体。品牌字、运营 banner、固定营销标题,则直接图片化。

这个分层方案也更接近电商大厂的真实取舍:它们通常不会追求每一段动态正文在所有平台像素级相同,而是把视觉控制力放在品牌、价格、利益点和运营素材上。

关键过程

微信和 uni-app 都提供了动态加载字体的能力。uni.loadFontFace 文档说明,它可以用于“使用自己的字体,而不是 OS 提供的字体,来渲染整个界面的文字”,但微信小程序端字体链接需要 HTTPS,并且要同源或开启 CORS;文档也提醒中文字体体积过大时建议抽离部分中文或用图片替代。参考:uni.loadFontFace 文档

腾讯云的小程序字体 API 文档也记录了类似限制:字体文件需要正确的 content-type,真机兼容性要求 HTTPS,字体资源要同源或允许 servicewechat.com 跨域,推荐 TTF 和 WOFF,低版本 iOS 上 WOFF2 可能不兼容。参考:Tencent Cloud wx.loadFontFace 文档

再看微信官方设计体系的方向,WeUI for 小程序强调的是“同微信原生视觉体验一致”,它是为微信内网页和小程序设计的基础样式库。这里的“统一”更偏平台体验统一,而不是用业务方字体覆盖所有系统文字。参考:WeUI for 小程序

因此,比较稳的技术结构是先定义字体变量:

page {
  --font-body: -apple-system, BlinkMacSystemFont, "PingFang SC",
    "Microsoft YaHei", Arial, sans-serif;
  --font-display: var(--font-body);
  --font-number: var(--font-body);
}

.font-ready {
  --font-display: "BrandDisplay", var(--font-body);
  --font-number: "BrandNumber", var(--font-body);
}

.price,
.date-large,
.stat-number {
  font-family: var(--font-number);
}

.section-title,
.primary-button {
  font-family: var(--font-display);
}

然后在 App.vueapp.js 里加载关键字体:

uni.loadFontFace({
  global: true,
  family: 'BrandNumber',
  source: 'url("https://cdn.example.com/fonts/brand-number.woff")',
  success() {
    // 设置全局状态,让根节点加 font-ready
  }
})

踩坑

第一个坑是把“视觉一致”理解成“全站同一字体”。中文全量字体文件体积很大,小程序加载失败、首屏闪动、网络慢、CORS 配错、低版本兼容问题都会被放大。全站切字体看似统一,实际可能换来更大的性能和稳定性风险。

第二个坑是把开发者工具当真机标准。Windows 开发工具里的字体和 Android 真机里的字体文件、抗锯齿、字重计算都不同。开发工具只能用于调试布局,最终验收仍要看目标真机矩阵。

第三个坑是想用 canvas 解决所有文字。canvas 可以更接近像素级一致,但动态文字的换行、可访问性、截断、省略、国际化、点击区域和后期维护都会变复杂。只有关键海报、分享图、固定视觉资产值得这么做。

第四个坑是忽略授权。思源黑体、Noto Sans SC、阿里巴巴普惠体、HarmonyOS Sans 等都可以作为候选,但商用授权、二次分发、子集化后的命名和托管方式要逐项确认,不能只看“网上能下载”。

最后方案

推荐按三层落地。

第一层是系统正文层:正文、表单、列表、说明文案使用系统字体栈,设计稿里不要卡死字宽。按钮、标签、列表项要靠 min-width、稳定 line-height、弹性布局和合理留白容错。

第二层是关键视觉层:价格、日期、统计数字、Tab、标题、主按钮短文案接入子集化字体。数字字体可以优先考虑 DIN 类、Roboto Condensed、Barlow Condensed 或品牌自有数字字体;中文关键字只抽取运营高频字和固定栏目字。

第三层是素材层:Logo、固定活动标题、banner 利益点、分享海报标题图片化。大厂电商常见的视觉一致性主要靠这一层和第二层,而不是靠全站正文同一字体。

如果产品经理或设计师问“能不能 100% 一样”,答案应该拆开说:普通动态 UI 不追求像素级一致,关键视觉位可以接近一致,固定运营视觉可以做到一致。

以后怎么做

以后做小程序字体验收,可以先把页面文字分成四类:正文、数字、短标题、运营素材。只有后三类需要讨论自定义字体或图片化。

技术上先接一套字体变量,不要把字体名散落在各页面里。加载成功后再切换 font-ready,加载失败就回退系统字体,避免空白或抖动扩大。

验收上至少覆盖 Windows 开发者工具、Android 微信真机和 iOS 微信真机。验收标准也要写清楚:哪些位置允许系统差异,哪些位置必须与设计图接近,哪些固定视觉必须完全一致。

这件事最好的边界不是“把字体栈写到最长”,而是把一致性的预算花在用户真正会感知的地方。