
Escribí eso usando en línea <svg>
los iconos constituyen el mejor sistema de iconos. Sigo pensando que eso es cierto. Es la forma más fácil de colocar un icono en una página. Sin solicitud de red, perfectamente estilizado.
Pero el código integrado tiene algunos inconvenientes, uno de los cuales es que no aprovecha el almacenamiento en caché. Estás haciendo que el navegador lea y procese el mismo código una y otra vez mientras navegas. No es un gran problema. Hay peces de mayor rendimiento para freír, ¿verdad? Pero sigue siendo divertido pensar en patrones más eficientes.
Scott Jehl escribió que el hecho de que inserte algo en línea no significa que no pueda almacenarlo en caché. Veamos si la idea de Scott se puede extender a los íconos SVG.
Comenzando con SVG en línea
Como esto…
<!DOCTYPE html>
<html lang="en">
<head>
<title>Inline SVG</title>
<link rel="stylesheet" href="https://css-tricks.com/styles/style.css">
</head>
<body>
...
<svg width="24" height="24" viewBox="0 0 24 24" class="icon icon-alarm" xmlns="http://www.w3.org/2000/svg">
<path id="icon-alarm" d="M11.5,22C11.64,22 11.77,22 11.9,21.96C12.55,21.82 13.09,21.38 13.34,20.78C13.44,20.54 13.5,20.27 13.5,20H9.5A2,2 0 0,0 11.5,22M18,10.5C18,7.43 15.86,4.86 13,4.18V3.5A1.5,1.5 0 0,0 11.5,2A1.5,1.5 0 0,0 10,3.5V4.18C7.13,4.86 5,7.43 5,10.5V16L3,18V19H20V18L18,16M19.97,10H21.97C21.82,6.79 20.24,3.97 17.85,2.15L16.42,3.58C18.46,5 19.82,7.35 19.97,10M6.58,3.58L5.15,2.15C2.76,3.97 1.18,6.79 1,10H3C3.18,7.35 4.54,5 6.58,3.58Z"></path>
</svg>
Es extrañamente fácil lanzar texto en la memoria caché del navegador como un archivo
En el HTML anterior, el selector .icon-alarm
nos traerá la porción completa de SVG para ese ícono.
const iconHTML = document.querySelector(".icon-alarm").outerHTML;
Luego, podemos introducirlo en la caché del navegador de esta manera:
if ("caches" in window) {
caches.open('static').then(function(cache) {
cache.put("/icons/icon-wheelchair.svg", new Response(
iconHTML,
{ headers: {'Content-Type': 'image/svg+xml'} }
));
}
}
Ver la ruta del archivo /icons/icon-wheelchair.svg
? Eso es un poco inventado. Pero realmente se guardará en la caché en esa ubicación.
Asegurémonos de que el navegador extraiga ese archivo de la caché cuando se lo solicite.
Registraremos un trabajador del servicio en nuestras páginas:
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/sw.js', {
scope: "https://css-tricks.com/"
});
}
El trabajador del servicio en sí será bastante pequeño, solo un comparador de caché:
self.addEventListener("fetch", event => {
let request = event.request;
event.respondWith(
caches.match(request).then(response => {
return response || fetch(request);
})
);
});
Pero … nunca solicitamos ese archivo, porque nuestros íconos están en línea.
Verdadero. Pero, ¿y si otras páginas se beneficiaran de ese caché? Por ejemplo, un icono SVG podría colocarse en la página de esta manera:
<svg class="icon">
<use xlink:href="https://css-tricks.com/icons/icon-alarm.svg#icon-alarm" />
</svg>
Ya que /icons/icon-alarm.svg
está listo en el caché, el navegador felizmente lo sacará del caché y lo mostrará.
(Me sorprendió un poco que esto funcionara. A Edge no le gusta <use>
elementos que se vinculan a archivos, pero eso terminará pronto. Actualizar, se acabó, Edge pasó a Chromium).
E incluso si el archivo no está en la caché, asumiendo que realmente lo arrojamos al sistema de archivos, probablemente sea el resultado de algún tipo de “inclusión” (utilicé Nunjucks en la demostración).
Pero… <use>
y SVG en línea no son lo mismo
Verdadero. Lo que me gusta de lo anterior es que está haciendo uso del caché y los íconos deberían mostrarse casi de inmediato. Y hay algunas cosas que puede diseñar de esta manera, por ejemplo, configurar el relleno en el ícono principal debe pasar por el DOM de sombra que el <use>
crea y colorea los elementos SVG dentro.
Aun así, no es lo mismo. El DOM de sombra es una gran barrera en comparación con SVG en línea.
¡Así que mejórelos! Podríamos cargar de forma asincrónica un script que encuentre cada ícono SVG, Ajaxs para el SVG que necesita, y reemplaza el <use>
cosas…
const icons = document.querySelectorAll("svg.icon");
icons.forEach(icon => {
const url = icon.querySelector("use").getAttribute("xlink:href"); // Might wanna look for href also
fetch(url)
.then(response => response.text())
.then(data => {
// This is probably a bit layout-thrashy. Someone smarter than me could probably fix that up.
// Replace the <svg><use></svg> with inline SVG
const newEl = document.createElement("span");
newEl.innerHTML = data;
icon.parentNode.replaceChild(newEl, icon);
// Remove the <span>s
const parent = newEl.parentNode;
while (newEl.firstChild) parent.insertBefore(newEl.firstChild, newEl);
parent.removeChild(newEl);
});
});
Ahora, asumiendo que este JavaScript se ejecuta correctamente, esta página tiene SVG en línea disponible al igual que la página original.
Demo y repositorio
- Demostración en Netlify
- Repo en GitHub