Inline SVG… Ge-cacht

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ich habe das mit Inline-<svg>-Icons geschrieben, um das beste Icon-System zu schaffen. Ich denke immer noch, dass das stimmt. Es ist der einfachste Weg, ein Icon auf eine Seite zu bringen. Kein Netzwerkaufruf, perfekt gestaltbar.

Aber das Inline-Einbetten von Code hat einige Nachteile, einer davon ist, dass es das Caching nicht nutzt. Der Browser muss denselben Code immer wieder lesen und verarbeiten, während Sie herumstöbern. Nicht, dass das eine große Sache wäre. Es gibt viel größere Performance-Probleme zu lösen, oder? Aber es macht immer noch Spaß, über effizientere Muster nachzudenken.

Scott Jehl schrieb, dass nur weil man etwas inline einfügt, heißt das nicht, dass man es nicht cachen kann. Mal sehen, ob Scotts Idee auf SVG-Icons erweiterbar ist.

Beginn mit Inline-SVG

So wie hier...

<!DOCTYPE html>
<html lang="en">

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

Es ist seltsam einfach, Text als Datei im Browser-Cache abzulegen

Im obigen HTML ruft der Selektor .icon-alarm uns den gesamten SVG-Code für dieses Icon ab.

const iconHTML = document.querySelector(".icon-alarm").outerHTML;

Dann können wir es auf diese Weise im Cache des Browsers ablegen

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'} }
    ));
  }
}

Sehen Sie den Dateipfad /icons/icon-wheelchair.svg? Der ist irgendwie erfunden. Aber er wird tatsächlich an diesem Ort im Cache abgelegt.

Stellen wir sicher, dass der Browser diese Datei aus dem Cache holt, wenn sie angefordert wird

Wir registrieren einen Service Worker auf unseren Seiten

if (navigator.serviceWorker) {   
  navigator.serviceWorker.register('/sw.js', {
    scope: '/'
  });
}

Der Service Worker selbst wird ziemlich klein sein, nur ein Cache-Matcher

self.addEventListener("fetch", event => {
  let request = event.request;

  event.respondWith(
    caches.match(request).then(response => {
      return response || fetch(request);
    })
  );
});

Aber... wir fordern diese Datei nie an, weil unsere Icons inline sind.

Stimmt. Aber was, wenn andere Seiten von diesem Cache profitieren würden? Zum Beispiel könnte ein SVG-Icon wie folgt auf der Seite platziert werden

<svg class="icon">
  <use xlink:href="/icons/icon-alarm.svg#icon-alarm" /> 
</svg>

Da /icons/icon-alarm.svg bereit im Cache liegt, wird der Browser es gerne aus dem Cache holen und anzeigen.

(Ich war ziemlich erstaunt, dass das funktioniert. Edge mag <use>-Elemente, die auf Dateien verlinken, nicht, aber das wird bald vorbei sein. Update, es ist vorbei, Edge ist auf Chromium umgestiegen.)

Und selbst wenn die Datei nicht im Cache ist, vorausgesetzt, wir legen diese Datei tatsächlich auf das Dateisystem, ist das wahrscheinlich das Ergebnis einer Art von „Include“ (ich habe Nunjucks im Demo verwendet).

Aber... <use> und Inline-SVG sind nicht ganz dasselbe

Stimmt. Was mir am obigen gefällt, ist, dass es den Cache nutzt und die Icons fast sofort gerendert werden sollten. Und es gibt einige Dinge, die Sie auf diese Weise gestalten können – zum Beispiel sollte das Setzen der Füllung des übergeordneten Icons durch den Shadow DOM gehen, den das <use>-Element erstellt, und die SVG-Elemente darin einfärben.

Dennoch ist es nicht dasselbe. Der Shadow DOM ist eine große Hürde im Vergleich zu Inline-SVG.

Also, verbessern wir sie! Wir könnten ein Skript asynchron laden, das jedes SVG-Icon findet, per Ajax das benötigte SVG abruft und den <use>-Kram ersetzt...

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

Nun, vorausgesetzt, dieser JavaScript-Code wird korrekt ausgeführt, verfügt diese Seite über Inline-SVG, genau wie die ursprüngliche Seite.

Demo & Repo