PostsMapsLinks
造物 / Make

🔤 Fontize

按实际渲染文本裁剪字体的 Nuxt 工具,CJK 字体从 17MB 裁到 50KB

简介

Fontize 是字体子集工具包:组件里声明哪些文本会被渲染,它就把字体裁到只含这些字符。 CJK 字体通常从 ~17MB 裁到 ~50KB,剩 0.3%。有在线演示

pnpm monorepo,三个包:@fontize/core(文本收集、子集化引擎、内容寻址缓存)、 @fontize/vueuseFontSubset 组合式函数)、@fontize/nuxt(Nuxt 模块,收集端点、产物输出、HTML 注入)。要求 Nuxt 4.5+。

用法

nuxt.config.ts 注册模块和字体,同别名可以挂多个字重:

export default defineNuxtConfig({
  modules: ['@fontize/nuxt'],
  fontize: {
    fonts: [
      { alias: 'noto-serif', src: './fonts/NotoSansSC.ttf' },
      { alias: 'noto-serif', src: './fonts/NotoSansSC-Bold.ttf', weight: 700 },
    ],
  },
})

组件里用 useFontSubset 声明文本,支持 getter 形式接异步数据:

<script setup lang="ts">
const { data } = useAsyncData('txt', () => $fetch('/api/title'))
const { text } = useFontSubset(() => data.value ?? '', 'noto-serif')
</script>

<template>
  <h1 style="font-family: 'noto-serif', serif" v-text="text" />
</template>

文本变化按防抖重新收集(默认 300ms 可配):dev 下重建子集并热更新,SSG 下由渲染完成后的重估兜底。

工作原理

dev 下 SSR 跑在 vite module runner 的隔离 realm,进程内桥不可用,所以 SSR 和浏览器统一走 HTTP 回环上报文本。 更新走 HMR 而非整页刷新:client 收到 fontize:updated 后热替换 fonts.css 的 link href, 新 @font-face 指向新 hash woff2(内容寻址规避缓存),组件状态不丢。

build / SSG 时 prerender 在主进程执行,进程内直接收集。字符集在 prerender 完成时达终态, 此刻生成 hash 命名的最终产物进 .output/public

使用边界

  • 生产 SSR 的动态文本(如 CMS 新文章)不在构建期收集范围内,会缺字;兜底是 include 配置注入常用字表
  • 同一 alias 的字符集是全站页面并集,不按路由拆分
  • 浏览器端生产环境 useFontSubset 是 noop,运行时新文本不会扩展子集

https://github.com/Lionad-Morotar/fontize

Copyright © 2024 Lionad - CC-BY-NC-CD-4.0