用 localstorage 记录字体子集哈希、语言标识、渲染标记和降级偏好,配合 font-display: swap 与 document.fonts.load() 实现平滑加载,避免 foit/fout;需序列化存取、try-catch 解析、null 判断存在性,并处理同源限制、storage 事件同步及无痕模式降级。

用 localStorage 记录字体子集加载状态,能有效缓解 FOIT(Flash of Invisible Text)和 FOUT(Flash of Unstyled Text)带来的文字闪烁问题。核心思路是:首次加载后存下已成功渲染的字体子集标识,后续访问直接跳过重复请求、复用已有资源,并配合 CSS font-display 策略实现平滑过渡。
存什么:关键字体状态信息
只存轻量、可验证的元数据,避免冗余:
-
子集哈希值:如
localStorage.setItem('font-subset-hash-zh', 'a1b2c3'),服务端生成子集时附带唯一哈希,前端比对一致则跳过加载; -
语言/区域标识:如
localStorage.setItem('user-lang', 'zh-Hans'),用于后续请求对应字形子集(如简体中文常用3500字),不硬编码字体文件名; -
关键文本渲染标记:如
localStorage.setItem('font-loaded-title', 'true'),表示标题类文字已完成字体替换,页面可提前启用自定义字体; -
用户降级偏好:如
localStorage.setItem('font-priority', 'clarity')或'speed',决定子集粒度(全字库 vs 高频字),影响首次渲染速度与后续清晰度平衡。
怎么存:安全写入 + 类型兜底
localStorage 只接受字符串,对象必须序列化,且要防解析失败:
- 存对象用
JSON.stringify(),例如:localStorage.setItem('font-config', JSON.stringify({ lang: 'zh-Hans', priority: 'clarity' })); - 读取时加
try...catch,并设默认值:let config = { lang: 'en', priority: 'speed' };<br>const raw = localStorage.getItem('font-config');<br>if (raw) try { config = JSON.parse(raw); } catch (e) { console.warn('font-config parse failed'); }; - 别用
if (localStorage.getItem('x'))判断存在性——"false"、"0"都是真值,应改用localStorage.getItem('x') !== null。
怎么用:结合 CSS 与 JS 渲染流程
状态不是存完就结束,得在实际加载链路中触发判断:
- 页面加载初期,先检查
font-subset-hash-zh是否存在且匹配当前所需子集;若匹配,直接document.fonts.load()已缓存字体,不发新请求; - 使用
font-display: swap保证文字始终可见,再通过document.fonts.load()+localStorage标记控制何时切换为自定义字体; - 监听
storage事件,在其他标签页更新字体状态时同步当前页行为(注意:触发修改的标签页自身不会收到该事件); - 无痕模式或 Safari 私密浏览下,
localStorage可能静默失败,需 fallback 到基础字体或捕获QuotaExceededError后降级处理。
注意事项:别踩这些坑
字体子集 + localStorage 是优化手段,不是万能解:
- 同源限制严格:http 和 https、不同端口、子域名之间数据完全隔离,跨环境测试务必覆盖;
- 不自动同步:localStorage 修改不会实时广播到同域其他标签页,必须靠
storage事件手动响应; - 容量虽有 5–10MB,但字体子集哈希等元数据建议控制在 KB 级,别塞 Base64 字体文件或整段 CSS;
- 敏感操作如清除缓存(
localStorage.clear())不可逆,线上环境慎用,建议按 key 精细删除。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











