Перевод статьи Osvaldas Valutis «Caching SVG Sprite in localStorage».
Есть два способа использования SVG в HTML через <use>: с внешним источником или заинлайненым. «Элемент use берёт узлы SVG-документа и дублирует их где-нибудь» — MDN. В первом случае вставка SVG обычно выглядит так:
<svg><use xlink:href="sprite.svg#cart"></use></svg>
Плюсы:
- Файл sprite.svg может быть закэширован браузером.
Минусы:
- Такой способ не работает в IE11- (к счастью, есть JS-фолбэк).
- Вы должны повторять путь к файлу при каждой вставке.
Во втором случае, о котором мы будет говорить в этом посте, SVG встроен в страницу. Обратите внимание, используется symbol. Это означает, что достаточно определить viewbox всего один раз. К тому же, определение SVG должно быть выше его использования через use, иначе в некоторых браузерах будут проблемы.
<body>
<svg style="display: none;">
<symbol id="svg-cart" viewbox="0 0 50 50">
<path d="..." /></symbol>
</svg>
<!-- ... -->
<svg><use xlink:href="#svg-cart"></use></svg>
<!-- ... -->
</body>
Плюсы:
- Работает в IE9+.
- Не надо постоянно повторять путь к файлу (который, обычно, бывает довольно длинным).
<!-- Это выглядит лучше... -->
<svg><use xlink:href="#svg-cart"></use></svg>
<!-- ...чем это -->
<svg><use xlink:href="theme/something/assets/img/sprite.svg#cart"></use></svg>
Минусы:
- SVG является частью страницы, поэтому не может быть закэширован браузером. Это означает, что его нельзя переиспользовать на других страницах.
А что, если SVG всеит много? 100кб или больше. Это означает, что на каждой странице вам придётся дополнительно загружать 100кб, вместо того, чтобы закэшировать их и переиспользовать. К примеру, если пользователь просмотрит на вашем сайте 11 страниц с мобильника — он потратит лишний мегабайт, что довольно плохо. Итак, можем ли мы закэшировать SVG-спрайт и в то же время избежать постоянного дублирования пути к файлу?
localStorage
Да! localStorage позволяет хранить часть страницы. Хранилище ограничено 5Мб для домена. Этого лостаточно, чтобы хранить спрайт.
Я написал небольшой кусочек JS для этого (~444 байта в минифицированном виде, без зависимостей). Вот как он работает:
При первой загрузке сайта:
- Считывает SVG.
- Вставляет его на страницу.
- Сохраняет его в localStorage.
При последующих загрузках:
- Читает информацию из localStorage.
- Вставляет SVG на страницу.
Если localStorage не поддерживается, переполнен или отключён, скрипт всё равно считывает SVG-файл и вставляет его на страницу.
;(function(window, document){
'use strict';
var file = 'img/svg.html',
revision = 1;
if(!document.createElementNS || !document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect)
return true;
var isLocalStorage = 'localStorage' in window && window['localStorage'] !== null,
request,
data,
insertIT = function(){
document.body.insertAdjacentHTML('afterbegin', data);
},
insert = function(){
if( document.body ) insertIT();
else document.addEventListener('DOMContentLoaded', insertIT);
};
if(isLocalStorage && localStorage.getItem('inlineSVGrev') == revision){
data = localStorage.getItem('inlineSVGdata');
if(data){
insert();
return true;
}
}
try{
request = new XMLHttpRequest();
request.open('GET', file, true);
request.onload = function(){
if(request.status >= 200 && request.status < 400){
data = request.responseText;
insert();
if(isLocalStorage){
localStorage.setItem('inlineSVGdata', data);
localStorage.setItem('inlineSVGrev', revision);
}
}
}
request.send();
}
catch(e){}
}(window, document) );
Вы можете вставить этот код в любое место на странице, так как SVG-файл считыватся асинхронно. Но если вы вставите его в тег head вашего сайта, иконки загрузятся чуть быстрее. Протестируйте, чтобы выбрать оптимальный для вас вариант.
Файл и его версия
Есть две строки, которые вам нужно поправить под себя:
var file = 'img/svg.html',
revision = 1;
file
— это путь к SVG-файлу. Это может быть любой svg-файл, но я лично не сохраняю свой спрайт как SVG, а вместо этого использую html. Мой sprite.html обычно выглядит так:
<svg style="display: none;" aria-hidden="true">
<symbol id="svg-plane" viewbox="0 0 510 510"><path d="..." /></symbol>
<symbol id="svg-close" viewbox="0 0 357 357"><path d="..." /></symbol>
<symbol id="svg-fav" viewbox="0 0 510 510"><path d="..." /></symbol>
<symbol id="svg-share" viewbox="0 0 459 459"><path d="..." /></symbol>
<symbol id="svg-cart" viewbox="0 0 510 510"><path d="..." /></symbol>
<symbol id="svg-tick" viewbox="0 0 510 510"><path d="..." /></symbol>
</svg>
revision
— это версия спрайта, которая говорит скрипту нужно ли заново загрузить спрайт и обновить его в localStorage. Если вы обновили спрайт, то вы просто обновляете и его версию, чтобы изменения применились на сайте.
Можете обновлять его вручную или автоматически, через PHP (Perl, Python или любой другой серверный язык, который вы используете). Если вы вставили этот JS-код прямо в HTML (который находится в php-файле), то вы можете использовать PHP-функцию, которая возвращает дату модификации файла (в формате Unix timestamp):
revision = <?=filemtime( 'img/svg.html' )?>;
<html>
<head>
<script>var INLINE_SVG_REVISION = <?=filemtime( 'img/svg.html' )?>;</script>
</head>
<body>
<!-- ... -->
<script src="common.js"></script>
</body>
</html>
Не поддерживается SVG?
Если браузер не поддерживает SVG, то мы можем положиться на растровые изображения и JS-фолбэк. Нам нужен атрибут data-img
, чтобы указать путь к растровой картинке:
<li><svg><use xlink:href="#svg-cart" data-img="img/cart.png"></use></svg></li>
Фолбэк сконвертирует эту строку в это:
<li><img src="img/cart.png" alt="" /></li>
Затем, если вы не используете html5shiv, вы создадите элементы svg и use, которые позднее будет использовать фолбэк. Этот код лучше всего расположить в начале тега head:
if( !document.createElementNS || !document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect )
{
document.createElement( 'svg' );
document.createElement( 'use' );
}
И наконец, сам фолбэк, который можно вставить в конце страницы (я позаимствовал некоторый код из svg4everybody):
;( function( window, document )
{
if( document.createElementNS && document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect ) return true;
var uses = document.getElementsByTagName( 'use' ), use;
while( ( use = uses[ 0 ] ) )
{
var svg = use.parentNode, img = new Image();
img.src = use.getAttribute( 'data-img' );
svg.parentNode.replaceChild( img, svg );
}
}( window, document ) );
JS отключён?
Самое худшее то, что вы не сможете использовать спрайт, если в браузере отключён JS. Но, как и в случае с отсутствием поддержки SVG, вы можете положиться на растровые картинки:
<li><svg><use xlink:href="#svg-cart"></use></svg><noscript><img src="img/cart.png" alt="" /></noscript></li>
Демо
Я соединил всё вышесказанное демо, ссылка на которую находится чуть ниже. Вы можете просмотреть код и использовать этот способ в своей работе.
P.S. В этой статье я сфокусировался на кэшировании SVG и не рассказал о том, как сделать ваш спрайт доступным. Я призываю вас рассмотреть советы по улучшению доступности, прежде чем использовать этот код в реальной жизни.
Поделиться