Schriftoptimierung für Astro-Websites
LaunchFast Logo LaunchFast

Schriftoptimierung für Astro-Websites

Rishi Raj Jain
astro-font by LaunchFa.st

In ca. einem Monat ist „astro-font“ auf 57.000 Downloads angewachsen 🤯

Hochwertige Starter-Kits mit integriertem Authentifizierungsfluss (Auth.js), Objekt-Uploads (AWS, Clouflare R2, Firebase Storage, Supabase Storage), integrierten Zahlungen (Stripe, LemonSqueezy), E-Mail-Verifizierungsablauf (Resend, Postmark, Sendgrid) und viel mehr . Kompatibel mit jeder Datenbank (Redis, Postgres, MongoDB, SQLite, Firestore).

Kontrollpunkt Nr. 1

Alles begann damit, dass ich darüber nachdachte, was im Astro-Ökosystem fehlt. Nachdem ich mir einige der Websites angesehen hatte (einschließlich meiner eigenen Hybrid-Astro-Website, launchfast), stellte ich fest, dass eine umfassende Bibliothek zur Schriftartenoptimierung fehlte 👇

Kontrollpunkt Nr. 2

Also machte ich mich tatsächlich daran, ein Astro-Paket zu erstellen, das das Versprechen hält, von dem ich dachte, dass es ganz einfach sein würde, einfach die Skripte zu verwenden, die @vercels „next/font“ macht, und es zu versenden (und das habe ich ganz am Anfang gemacht!) Bei statischen Websites hat es hervorragend funktioniert! ABER, betreten Sie SSR-Websites👇

Straßensperre Nr. 1

Da „next/font“ mit dem Erstellungsprozess von Next gekoppelt ist, hat es Zugriff auf die Ausgabeverzeichnisse und die erwartete Laufzeitkonfiguration und hostet daher Schriftarten selbst auf SSR-First-Websites. Dieser Bereich wurde für Astro SSR-Websites komplexer, da „Astro- „font“ ist eine Astro-Komponente und keine Astro-Integration! Hier ist, was ich getan habe, um dieses Problem zu lösen 👇

async function getOS(): Promise<typeof import('node:os') | undefined> {
let os
try {
os = await import('node:os')
return os
} catch (e) {}
}
// Check if writing is permitted by the file system
async function ifFSOSWrites(dir: string): Promise<string | undefined> {
try {
const fs = await getFS()
if (fs) {
const testDir = join(dir, '.astro_font')
if (!fs.existsSync(testDir)) fs.mkdirSync(testDir)
fs.rmSync(testDir, { recursive: true, force: true })
return dir
}
} catch (e) {}
}

Straßensperre Nr. 2

Großartig! Das funktionierte und ermöglichte es mir, festzustellen, ob im SSR-Build Schriftarten enthalten waren, und ermöglichte mir so, die Fallback-Schriftart zur Laufzeit zu berechnen. ABER einige Benutzer wollten CDN-URLs verwenden oder verwendeten Fontsource-Schriftarten. Es gab keine Möglichkeit, dies zu wissen was Vite hat die internen Schriftarten aufgelöst? Daher habe ich einen Laufzeit-CSS-Parser wie Google Fonts erstellt 👇

// Custom script to parseGoogleCSS
function parseGoogleCSS(tmp: string) {
let match
const fontFaceMatches = []
const fontFaceRegex = /@font-face\s*{([^}]+)}/g
while ((match = fontFaceRegex.exec(tmp)) !== null) {
const fontFaceRule = match[1]
const fontFaceObject: any = {}
fontFaceRule.split(';').forEach((property) => {
if (property.includes('src: ')) {
const formatPosition = property.indexOf('for')
fontFaceObject['path'] = property
.trim()
.substring(9, formatPosition ? formatPosition - 5 : property.length - 1)
.trim()
}
if (property.includes('-style: ')) {
fontFaceObject['style'] = property.split(':').map((i) => i.trim())[1]
}
if (property.includes('-weight: ')) {
fontFaceObject['weight'] = property.split(':').map((i) => i.trim())[1]
}
if (property.includes('unicode-range: ')) {
if (!fontFaceObject['css']) {
fontFaceObject['css'] = {}
}
fontFaceObject['css']['unicode-range'] = property.split(':').map((i) => i.trim())[1]
}
})
fontFaceMatches.push(fontFaceObject)
}
return fontFaceMatches
}

Kontrollpunkt Nr. 3

Scheint vollständig zu sein, oder? Es funktioniert jetzt mit lokalen Schriftarten und Schriftarten über CDN. Aber Laufzeitabruf und -berechnung werden uns SSR-Zeit kosten. Um das zu lösen, geben Sie Laufzeit-Schriftarten-Caching ein 👇

const [os, fs] = await Promise.all([getOS(), getFS()])
if (fs) {
if (os) {
writeAllowed = await Promise.all([ifFSOSWrites(os.tmpdir()), ifFSOSWrites('/tmp')])
tmpDir = writeAllowed.find((i) => i !== undefined)
cacheDir = fontCollection.cacheDir || tmpDir
if (cacheDir) {
// Create a json based on slugified path, style and weight
const slugifyPath = (i: Source) => `${i.path}_${i.style}_${i.weight}`
const slugifiedCollection = fontCollection.src.map(slugifyPath)
const cachedFileName = simpleHash(slugifiedCollection.join('_')) + '.txt'
cachedFilePath = join(cacheDir, cachedFileName)
if (fs.existsSync(cachedFilePath)) {
try {
const tmpCachedFilePath = fs.readFileSync(cachedFilePath, 'utf8')
return JSON.parse(tmpCachedFilePath)
} catch (errorReadingCache) {}
}
}
}
}

Kontrollpunkt Nr. 4

Jetzt? Uns bleibt nur noch eine Sache zu tun: Erlauben Sie das Vorladen pro Schriftart und Konfiguration (und keine Rückwärtsunterstützung für globale Vorladungen)!

// If the parent preload is set to be false, look for true only preload values
if (fontCollection.preload === false) {
return fontCollection.src
.filter((i) => i.preload === true)
.map((i) => getRelativePath(getBasePath(fontCollection.basePath), i.path))
}
// If the parent preload is set to be true (or not defined), look for non-false values
return fontCollection.src
.filter((i) => i.preload !== false)
.map((i) => getRelativePath(getBasePath(fontCollection.basePath), i.path))

Und wir sind fertig und es ist für viele Astro-Websites in Produktion ✨

<typeof import(‘node:os’) | undefined><string | undefined>Vielen Dank für dieses tolle Paket, es hat mir geholfen, die „Layoutverschiebung“ zu bekämpfen!

Learn More Abfrage von Cloud Firestore mit Astro auf Cloudflare Workers
Abfrage von Cloud Firestore mit Astro auf Cloudflare Workers April 25, 2025
Abfrage von Redis mit Astro auf Cloudflare Workers
Abfrage von Redis mit Astro auf Cloudflare Workers April 25, 2025
Wie man vorab signierte URLs für Firebase Storage mit Astro auf Cloudflare Workers generiert
Wie man vorab signierte URLs für Firebase Storage mit Astro auf Cloudflare Workers generiert April 24, 2025