Optimización de fuentes para sitios web de Astro
LaunchFast Logo LaunchFast

Optimización de fuentes para sitios web de Astro

Rishi Raj Jain
astro-font by LaunchFa.st

En ~1 mes, astro-font ha aumentado a 57.000 descargas 🤯

Punto de control n.° 1

Todo comenzó cuando pensé en lo que le falta al ecosistema Astro. Después de revisar algunos de los sitios web (incluido mi propio sitio web híbrido Astro, launchfast), descubrí que faltaba una biblioteca de optimización de fuentes de extremo a extremo 👇

Punto de control #2

Así que me propuse crear un paquete Astro que cumpliera la promesa que pensé que sería súper fácil: usar los scripts next/font de @vercel y enviarlo (¡y lo hice desde el principio!) ¡Funcionó muy bien para sitios web estáticos! PERO, ingrese a los sitios web de SSR👇

Barricada #1

next/font, al estar acoplado con el proceso de compilación de Next, tiene acceso a los directorios de salida y a la configuración de tiempo de ejecución esperada y, por lo tanto, aloja automáticamente las fuentes incluso en los sitios web SSR-first. Este espacio se volvió más complejo para los sitios web de Astro SSR a medida que astro- font es un componente de Astro y no una integración de Astro. Esto es lo que hice para resolver ese problema 👇

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) {}
}

Barricada #2

¡Excelente! Eso funcionó y me permitió determinar si la compilación SSR tenía fuentes incluidas y, por lo tanto, me permitió calcular la fuente alternativa en el tiempo de ejecución, PERO, algunos usuarios querían usar URL de CDN o estaban usando fuentes de fuente. No había forma de saberlo. qué Vite resolvió las fuentes internas. Por lo tanto, construí un analizador CSS en tiempo de ejecución similar a Google Fonts 👇

// 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
}

Punto de control n.º 3

Parece completo, ¿verdad? Ahora funciona con fuentes locales y fuentes a través de CDN, pero la búsqueda y computación en tiempo de ejecución nos costará tiempo SSR. Para resolver eso, ingrese el almacenamiento en caché de fuentes en tiempo de ejecución 👇

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) {}
}
}
}
}

Punto de control #4

¿Ahora? ¡Solo nos queda una cosa por hacer: Permitir precargas por fuente por configuración (y soporte hacia atrás para precargas globales (no))!

// 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))

Y terminamos, y está en producción para muchos sitios web de Astro

<typeof import(‘node:os’) | undefined><string | undefined>¡Gracias por ese increíble paquete que me ayudó a luchar contra el “cambio de diseño”!

Learn More Autenticación de Benutzern en Astro mit besserer Authentifizierung: Eine Schritt-für-Schritt-Anleitung
Autenticación de Benutzern en Astro mit besserer Authentifizierung: Eine Schritt-für-Schritt-Anleitung November 24, 2024
Astro vs.Next.js: Auswahl de los mejores frameworks en enero de 2024
Astro vs.Next.js: Auswahl de los mejores frameworks en enero de 2024 October 30, 2024
6 funciones diferentes, el kit de inicio web completo incluye todas las funciones
6 funciones diferentes, el kit de inicio web completo incluye todas las funciones October 26, 2024