背景
微信小程序里的字体一致性,经常会被误判成一个 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.vue 或 app.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 微信真机。验收标准也要写清楚:哪些位置允许系统差异,哪些位置必须与设计图接近,哪些固定视觉必须完全一致。
这件事最好的边界不是“把字体栈写到最长”,而是把一致性的预算花在用户真正会感知的地方。