In ca. einem Monat ist „astro-font“ auf 57.000 Downloads angewachsen 🤯
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 systemasync 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 parseGoogleCSSfunction 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 valuesif (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 valuesreturn 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!
Ich werde es zu Best of JS hinzufügen, da wir ein Tag „Astro“ haben [[ HTML_TAG]]https://tco/38Akqa5IQd[[HTML_TAG] ]
– Michael Rambeau (@michaelrambeau) 17. Januar 2024







