unicode-range 是字符路由规则而非按需加载开关,真正按需加载需手动拆分字体文件并分别声明同名 font-family;必须配合子集生成、preload、font-display: optional 及排版约束。

unicode-range 不是“按需加载开关”,而是字符路由规则
写了 unicode-range 字体却全量加载,不是你写错了,而是误以为它能触发分片下载。它只做一件事:告诉浏览器「这个 @font-face 规则对哪些 Unicode 码点生效」。真正实现按需加载,必须配合手动拆分字体文件——中文字形进 zh.woff2,英文字形进 en.woff2,再各自声明一条 @font-face,且 font-family 名完全一致。
常见错误是只写一条含 unicode-range 的规则,src 指向一个 3MB 的完整中英混合 WOFF2 文件。结果浏览器照单全收,哪怕页面只用到 5 个汉字。
- 验证是否真生效:DevTools → Elements → 选中文字 → Fonts 面板看「Actual font used」是不是你期望的子集字体名
- 漏掉
U+0020(空格)会导致中英文混排时断行异常,必须显式包含 - iOS Safari 对中文区间(如
U+4E00-9FFF)解析不稳定,建议补全标点:U+3000-303F(中文标点)、U+FF00-FFEF(全角 ASCII)
移动端必须拆开中英子集,不能靠 unicode-range “切”一个大文件
浏览器无法从单个 WOFF2 文件里按 unicode-range 动态提取字形;它只会整个下载。移动端尤其不能赌——3G 网络下 2MB 字体可能卡住首屏 8 秒以上。可行路径只有一条:用 fonttools 或 glyphhanger 提取实际用到的汉字(比如首页文案 + 常用按钮词),生成 ≤200KB 的 zh-subset.woff2;另用一套纯拉丁字符的 en-basic.woff2(覆盖 U+0020-007E 和 U+00A0-00FF)。
- 中文字体子集务必保留数字、ASCII 标点、常用符号(如
·、—、…),否则用户输入或后端返回内容会 fallback 失败 - 不要试图用
unicode-range把一个中文字体“逻辑切分”成多段——iOS Safari 可能直接忽略整条规则 - 如果页面含搜索框或评论区等动态内容,先加载基础子集,空闲时再 fetch 全量字体并注入新
@font-face
font-display: optional 是移动端防白屏的关键
font-display: swap 在移动端常导致明显 FOUT(字体闪动),尤其当子集字体加载延迟 >300ms 时。更稳妥的是 font-display: optional:浏览器只在字体能在 100ms 内就绪时才启用,否则直接用系统字体渲染,彻底规避阻塞。
- 必须搭配
<link rel="preload" href="zh-subset.woff2" as="font" type="font/woff2" crossorigin>,但仅限首屏必需字体 - 避免在
src中写多个格式(如url(x.woff2), url(x.woff)),移动端解析开销高,优先单写 WOFF2 - 不支持
font-display的旧 Android 浏览器(如 4.4 WebView)会回退到默认行为,需测试 fallback 是否可接受
中英混排时 line-height 和字宽差异必须提前锁定
即使子集字体加载成功,中文字体和英文字体的 baseline、x-height、字宽不同,仍会引发布局抖动——比如按钮文字突然变高撑开容器。这不是 unicode-range 能解决的问题,得靠 CSS 预设。
- 给所有文本容器显式设置
line-height、height和vertical-align,禁用font-size自适应缩放 - 用
ch或em单位统一宽度约束,避免中英文混排时因字宽差异导致换行错乱 - Flex/Grid 布局中慎用
align-items: center,不同字体的基线对齐偏差可能达 2–3px,改用padding微调更可控
真正难的不是写对 unicode-range,而是把字体拆得够细、fallback 覆盖够全、排版约束够硬——三者缺一,移动端体验就容易在某个机型上崩掉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











