直接用加载字体库会因无超时机制导致页面阻塞,应改用fetch+abortcontroller动态加载并设超时,配合font-display:swap和preload等优化措施确保字体可靠加载。

为什么直接用 <link rel="stylesheet"> 加字体库会卡住页面
浏览器对 <link> 标签加载 CSS(含字体声明)默认无超时机制,一旦 CDN 延迟、DNS 解析失败或代理拦截,请求可能挂起 10–30 秒甚至更久,期间 HTML 解析暂停、DOM 构建阻塞、首屏白屏——这不是 bug,是规范行为。
常见现象包括:
- Network 面板里看到某个
fonts.googleapis.com或use.typekit.net请求状态一直 pending - 页面 HTML 已下载完,但
内容迟迟不渲染 - 开发者工具里
DOMContentLoaded时间远大于资源实际大小应有耗时
用 fetch + AbortController 动态加载字体 CSS 并设超时
核心是绕过 <link> 的同步阻塞,改用 JS 异步拉取 CSS 内容,再注入 <style></style>。关键点不是“能不能加载”,而是“超时后要不要放弃”。
实操要点:
- 必须在
内尽早执行脚本(建议加defer,不放async) -
AbortController实例需在fetch调用前创建,并把signal传入fetch()选项 -
setTimeout的回调里调用controller.abort(),不能只靠fetch自身的 timeout 参数(它不存在) - 成功后用
response.text()获取 CSS 字符串,再创建<style></style>插入document.head - 失败时建议 fallback 到本地兜底 CSS(如内联基础排版规则),而非什么都不做
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 8000);
fetch('https://fonts.googleapis.com/css2?family=Inter:wght@400;700', {
signal: controller.signal
})
.then(res => res.text())
.then(css => {
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
})
.catch(err => {
if (err.name === 'AbortError') {
console.warn('字体 CSS 加载超时,跳过');
} else {
console.error('字体 CSS 加载失败', err);
}
})
.finally(() => clearTimeout(timeoutId));
font-display: swap 不生效的三个硬性前提条件
font-display 只在 @font-face 规则内部有效,且依赖字体文件真实被浏览器识别和请求。写了 swap 还白屏/跳动,大概率是以下任一环节断了:
-
src中漏写format("woff2"):浏览器无法预判字体类型,可能忽略整个@font-face,font-display彻底失效 - 没配
<link rel="preload" href="..." as="font" type="font/woff2" crossorigin>:字体请求等到 CSS 解析完才发起,弱网下 fallback 显示延迟达 1.5 秒以上 -
crossorigin属性缺失:即使写了preload,浏览器也会静默丢弃该请求(Network 面板里看不到),字体根本不会提前下载
Canvas 中用第三方字体必须等 document.fonts.load() 完成
Canvas 不会自动等待 Web Font 加载,context.font = "16px 'Inter'" 立即执行,但若字体未就绪,它会回退到系统默认字体,且不报错、不提示。
正确做法分三步,缺一不可:
- 先用
new FontFace('Inter', 'url(/fonts/inter.woff2)')创建实例 - 立刻调用
document.fonts.add(fontFace) - 再调用
document.fonts.load('16px "Inter"')—— 此时才真正发起网络请求
注意:document.fonts.load() 的 Promise resolve 表示字体已下载并解析完成,但首次绘制仍可能 FOIT(短暂空白),尤其在 Chrome 中。如需 100% 确保可用,应在 load() 后加一次 document.fonts.check('16px "Inter"') 验证。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











