Встроенный SVG… кэшируется

Использование встроенных <svg> — самый простой способ разместить иконку на странице. Нет сетевого запроса, идеально подходит для стилей.

Но у встроенного кода есть недостатки. Один из них – отсутствие  кэширования. Или это можно исправить?

Содержание

Начнем со встроенного SVG

Как этот…

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

Легко можно добавить текст в кэш браузера в виде файла

В приведенном ниже HTML-коде селектор .icon-alarm извлекает фрагмент <svg> для иконки.

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

Затем мы сможем вставить его в кэш браузера:

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

Видите путь /icons/icon-wheelchair.svg? Это файл будет помещен в кэш.

Удостоверимся, что браузер извлекает этот файл из кэша при запросе

Зарегистрируем Service Worker на страницах:

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

Сам Service Worker будет довольно небольшим:

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

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

Мы никогда не запрашиваем этот файл, потому что наши иконки встроены

Но другие страницы могут извлечь выгоду из этого. Например, SVG-иконка может быть размещена на странице следующим образом:

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

Поскольку /icons/icon-alarm.svg уже готов к кэшированию, браузер просто вытащит его из кэша и отобразит.

Но <use> и встроенный SVG не совсем то же самое

Описанный выше подход использует кэш, а иконки должны отображаться почти сразу. При этом лучше, если установка заливки для родительской иконки будет происходить через shadow DOM, который создает <use>. Поэтому элементы будут «окрашиваться» внутри него. Так что улучшайте это!

Мы могли бы асинхронно загрузить скрипт, который находит каждую иконку, загружает нужные SVG с помощью Ajax и заменяет элементы в <use>…

const icons = document.querySelectorAll("svg.icon");

icons.forEach(icon => {
  const url = icon.querySelector("use").getAttribute("xlink:href"); // Мы можем захотеть найти также href
  fetch(url)
    .then(response => response.text())
    .then(data => {
      // Кто-то умнее меня, возможно, сможет это исправить.

      // Заменяем <svg><use></svg> встроенным SVG
      const newEl = document.createElement("span");
      newEl.innerHTML = data;
      icon.parentNode.replaceChild(newEl, icon);

      // Удаляем <span>
      const parent = newEl.parentNode;
      while (newEl.firstChild) parent.insertBefore(newEl.firstChild, newEl);
      parent.removeChild(newEl);
    });
});

Предполагая, что приведенный выше JavaScript-код выполняется правильно, эта страница имеет встроенный SVG, как и оригинальная.

Демо-версии примеров:

  • На Netlify
  • На GitHub

Данная публикация представляет собой перевод статьи «Inline SVG… Cached» , подготовленной дружной командой проекта Интернет-технологии.ру