You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何仅在本地无对应字体时预加载Roboto字体?

解决方案:动态检测系统字体 + 按需预加载自定义Roboto

要实现仅在本地无San Francisco或Roboto时预加载自定义字体,同时避免不必要的资源浪费,我们可以结合JavaScript字体检测动态资源注入的方式,具体步骤如下:

1. 字体检测核心函数

首先需要一个函数来判断目标字体是否在本地可用。原理是通过比较同一文本在目标字体和基准字体(如sans-serif)下的渲染尺寸差异——如果尺寸不同,说明目标字体存在:

function isFontAvailable(fontFamily) {
  // 创建隐藏的测试元素,避免影响页面布局
  const testEl = document.createElement("span");
  testEl.style.cssText = `
    position: absolute;
    visibility: hidden;
    font-size: 100px;
    white-space: nowrap;
    font-family: sans-serif;
  `;
  document.body.appendChild(testEl);
  
  // 记录基准字体的尺寸
  const baseWidth = testEl.offsetWidth;
  
  // 切换到目标字体
  testEl.style.fontFamily = `"${fontFamily}", sans-serif`;
  const targetWidth = testEl.offsetWidth;
  
  // 清理元素
  document.body.removeChild(testEl);
  
  // 尺寸不同则字体存在
  return targetWidth !== baseWidth;
}

2. 按需预加载与字体配置

在页面加载早期(建议放在<head>中执行),检测系统字体情况,仅当San Francisco和系统Roboto都不存在时,动态添加预加载标签并配置自定义字体:

// 尽早执行,避免资源加载延迟
(function() {
  // 检测是否有苹果原生San Francisco字体
  const hasSF = isFontAvailable("-apple-system");
  // 检测是否有安卓系统自带Roboto字体
  const hasSystemRoboto = isFontAvailable("Roboto");

  if (!hasSF && !hasSystemRoboto) {
    // 动态创建预加载标签
    const preloadLink = document.createElement("link");
    preloadLink.rel = "preload";
    preloadLink.as = "font";
    preloadLink.href = "/assets/fonts/roboto.woff2";
    preloadLink.type = "font/woff2";
    preloadLink.crossOrigin = ""; // 同域资源也需添加,避免浏览器跨域限制

    document.head.appendChild(preloadLink);

    // 预加载完成后,注册自定义Roboto字体
    preloadLink.onload = function() {
      const fontStyle = document.createElement("style");
      fontStyle.textContent = `
        @font-face {
          font-family: 'CustomRoboto';
          src: url('/assets/fonts/roboto.woff2') format('woff2');
          font-display: swap; /* 优先显示系统字体,避免文本闪烁 */
          font-weight: normal;
          font-style: normal;
        }
        body {
          font: normal 1em -apple-system, Roboto, 'CustomRoboto', sans-serif;
        }
      `;
      document.head.appendChild(fontStyle);
    };
  } else {
    // 直接使用系统字体,无需加载自定义资源
    const systemFontStyle = document.createElement("style");
    systemFontStyle.textContent = `
      body {
        font: normal 1em -apple-system, Roboto, sans-serif;
      }
    `;
    document.head.appendChild(systemFontStyle);
  }
})();

3. 关键优化点

  • font-display: swap:确保字体加载完成前,页面先显示 fallback 字体,避免FOIT(不可见文本闪烁),提升用户体验。
  • 尽早执行脚本:将检测脚本放在<head>最顶部,确保在页面渲染前完成判断,避免资源加载延迟。
  • 避免全局预加载:仅在真正需要时才触发字体预加载,减少不必要的网络请求,降低页面体积。
  • 跨域处理:即使字体是同域资源,也需添加crossOrigin=""属性,否则部分浏览器会拒绝使用预加载的字体资源。

4. 备选方案:结合CSS @font-face 优先级

如果不想用JS检测,也可以通过CSS的字体优先级配置,让浏览器仅在系统字体不存在时加载自定义字体,但无法实现预加载(浏览器会在需要时才下载):

@font-face {
  font-family: 'Roboto';
  src: url('/assets/fonts/roboto.woff2') format('woff2');
  font-display: swap;
  font-weight: normal;
  font-style: normal;
  /* 降低自定义字体的优先级,让系统字体优先匹配 */
  font-family: 'Roboto', fallback;
}

body {
  font: normal 1em -apple-system, Roboto, sans-serif;
}

不过这种方式无法实现预加载,字体下载时机较晚,适合对加载速度要求不极致的场景。

内容的提问来源于stack exchange,提问作者Derek

火山引擎 最新活动