SVG para ícones é o que há atualmente em webdev. Mas usar SVG inline não nos permite aproveitar as maravilhas do cache… Ou será que isso é possível? 🤔
Veja como usar SVG inline com cache a unir o que de melhor é possível fazer em sistemas de ícones com SVG cacheados!
A questão do código inline para ícones em SVG
Muitos desenvolvedores (a maioria?) são a favor de usar código inline para ícones. Esta seria a maneira mais fácil de colocar um ícone em uma página: nenhuma requisição extra; perfeitamente estilizado.
Mas código inline tem algumas desvantagens, uma das quais é que não tira proveito do armazenamento em cache. O navegador tem que ler e processar o mesmo código tantas vezes quanto forem as visitas às páginas.
Não é tão ruim assim, certo? Certamente, há questões de performance muito mais importantes para nos preocuparmos. Mas, ainda assim, é divertido (neste caso, importante) pensar em padrões mais eficientes.
Scott Jehl escreveu um artigo mostrando que só porque você colocou um código inline, não significa que você não possa armazená-lo em cache. Veremos se a ideia de Scott pode ser aplicada também a ícones SVG.
Começando com SVG inline
Para começar com essa história toda de SVG inline com cache, veja este código de exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!doctype html> <html lang="pt-br"> <head> <title>Inline SVG</title> <link rel="stylesheet" href="/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> |
Uma parte de uma página HTML comum, sem mistério. No <body>
, a peça principal de nossa investigação, um SVG inline.
Como cachear SVG inline como um arquivo
No HTML acima, o seletor .icon-alarm
provê o pedaço (chunk) inteiro de <svg>
para aquele ícone.
1 |
const iconHTML = document.querySelector(".icon-alarm").outerHTML; |
Então, torna-se possível colocar isso no cache do navegador assim:
1 2 3 4 5 6 7 8 |
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'} } )); } } |
Vê o caminho /icons/icon-wheelchair.svg
? Isso é meio que apenas inventado. Mas realmente será colocado no cache naquele local (location).

Garantindo que o navegador use esse arquivo sem cache corretamente
Para o macete de usar SVG inline com cache dê certo, também é preciso registrar um Service Worker para as páginas:
1 2 3 4 5 |
if (navigator.serviceWorker) { navigator.serviceWorker.register('/sw.js', { scope: '/' }); } |
Que é bem pequeno, na verdade, servindo apenas como um correspondente de cache (cache matcher):
1 2 3 4 5 6 7 8 9 |
self.addEventListener("fetch", event => { let request = event.request; event.respondWith( caches.match(request).then(response => { return response || fetch(request); }) ); }); |
Mas… Esse arquivo SVG nunca é requisitado, já que é inline!
Essa afirmação é verdade, mas e se outras páginas também se beneficiassem desse cache?
Por exemplo, um ícone SVG poderia ser colocado na página assim:
1 2 3 |
<svg class="icon"> <use xlink:href="/icons/icon-alarm.svg#icon-alarm" /> </svg> |
Uma vez que /icons/icon-alarm.svg
está pronto para ser colocado em cache, o navegador irá arrancá-lo do cache e exibí-lo.
Mas… <use>
e inline SVG não são exatamente a mesma coisa
E há algumas coisas possíveis de serem estilizadas dessa maneira — por exemplo, definir o fill
do ícone-pai faz passar para Shadow DOM que o <use>
cria e colore os elementos SVG dentro dele.
Ainda assim, realmente não é a mesma coisa. Shadow DOM é uma grande barreira em comparação a SVG inline.
É possível, por exemplo, carregar de forma assíncrona um script que encontre cada ícone SVG, usa um Ajax para o SVG necessário e substitui o <use>
…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const icons = document.querySelectorAll("svg.icon"); icons.forEach(icon => { const url = icon.querySelector("use").getAttribute("xlink:href"); fetch(url) .then(response => response.text()) .then(data => { // Isso provavelmente pode atrapalhar um pouco o layout (layout-thrashy). // Algum nerd do JavaScript poderia consertar isso e enviar pra gente. <3 // Troca o <svg><use></svg> por inline SVG const newEl = document.createElement("span"); newEl.innerHTML = data; icon.parentNode.replaceChild(newEl, icon); // Remove os <span>s const parent = newEl.parentNode; while (newEl.firstChild) parent.insertBefore(newEl.firstChild, newEl); parent.removeChild(newEl); }); }); |
Agora, supondo que esse JavaScript seja executado corretamente, essa página tem o SVG inline disponível da mesma forma que a página original! ❤
Conclusão sobre SVG inline com cache!
Como visto, é possível sim usar inline SVG com cache!
Claro, adaptações e criações em código devem ser feitas e algumas tecnologias novas usadas, mas nada que seja absurdamente difícil.
Certamente, para os casos em que seja preciso usar sistemas de ícones em SVG com código inline, esta técnica está em alta consideração.