Schriftoptimierung für Astro-Websites

Black Friday: Enjoy a 25% discount on the starter kits. Use code BLACKFRIDAY2024 at checkout.

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 🤯

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 Astro vs. Next.js: Auswahl des richtigen Frameworks im Jahr 2024
Astro vs. Next.js: Auswahl des richtigen Frameworks im Jahr 2024 October 30, 2024
6 wesentliche Funktionen, die jedes Web-Starter-Kit enthalten sollte
6 wesentliche Funktionen, die jedes Web-Starter-Kit enthalten sollte October 26, 2024
Schneller Start mit Astro 4.16
Schneller Start mit Astro 4.16 October 23, 2024