Как стать автором
Обновить

Комментарии 16

>На этот раз удобный

А что не удобного в стандартном способе
import { ReactComponent as MyLogo } from './logo.svg';

А что у тебя прилетает в ReactComponent?

Если там Реакт-компонент (как я понимаю из контекста), подготовленный через SVGR, например, тогда все svg-изображения становятся частью js-бандла. Это по разным причинам не очень удобно, главная из которых - ты не можешь управлять их загрузкой. Плюс накладные расходы на рендеринг в Реакте.

А если там url до файла, получаемый через file-loader, то у тебя нет возможности встроить это svg на страницу так, чтобы работала стилизация через css.

При таком подходе не получиться нативно менять цвета или размеры изображения.

Получится. Через селектор `svg {}`

Ну вот представь что у тебя так вставляется иконка в элемент списка.

В списке рендерится например 20 строк и вот у тебя в итоге в html коде 20 раз одно и то же содержимое SVG файла. Которое может быть достаточно объемным. Растет Dom сильно.

Использование спрайтов и символов решает эту проблему.

P.s. именно лого обычно лучше встраивать через тег img

Тоже когда-то заморачивался и думал, что если 20 пунктов в списке - это 20 одинаковых иконок.. это плохо и нужно обязательно через спрайт и use всё это дело оформлять.

Однако потом пришло "прозрение" - что по сути иконки - это очень-очень простые фигуры.. как правило один короткий path.. ну может парочку. И поэтому смысла возится со спрайтом вроде как и нет.. ибо сами иконки всего чуть-чуть больше чем объвление тега use. И на 20-ти элементах списка выигрыша почти и нет.

Если же это иконка сложная - то скорее всего это уже не иконка, а какой-то отдельный элемент\иллюстрация, который встречается один раз на странице (лого там и др.).

По сути тег <use> - скорее изначально задумывался как хелпер для самих векторных редакторов, чтоб в сложной иллюстрации можно было бы переиспользовать повторяющиеся фигуры и комбинировать их.
Но потом кто-то придумал использовать этот эффект как возможность организации свг-спрайтов в html..))

Ой, а мы ведь нечто подобное делали несколько лет назад. Правда, без Реакта, на ванильном Джаваскрипте. Реакт тут и не при чём, по существу.

Да, все так, Реакт тут опционален, там в конце есть пример использования на ванильном :)

Гспди, зачем?!

Во-первых при всем этом не получится менять цвета и тд — т.к. SVG становится встроенным объектом (особенно при использовании svg-symbols) и придется поплясать с бубном, чтобы он стал обычным SVG в разметке и к его внутренностям можно было применять CSS.

Во-вторых все это делается крохотной функцией на vanila js, а не модулем ноды.

Картинка в тему

Интересно. Ндо будет попробовать. Спасибо за статью.

Действительно оно из лучших решений! А handy удобный инструмент для организации работы со спрайтами.
На деле представленные подходы можно и нужно комбинировать.
Картинки встречающиеся в приложении только один раз - инлайнить.
Используемые в нескольких местах картинки/иконки - брать из спрайта.
Статичные изображения не требующие модификации(ну типа лого в футере) - вставляем через обычный тег img
Что-то можно и в css через data-uri прописать.

Если по каким-то причинам не удается сменить цвет иконки через fill и store , можно заморочиться с фильтрами на css и подобрать любой цвет через hue-rotate, например

svg { filter: sepia() saturate(1000%) hue-rotate(0deg) }

Вот тут сравнивают перформанс разных способов втыкать svg: https://cloudfour.com/thinks/svg-icon-stress-test/

Не похоже чтобы спрайты как-то помогали с кучей повторяющихся иконок.

А у инлайна куча плюсов - он в CRA из коробки, ничего не моргает, грузится сразу все и только нужное. И даже подгружать можно по необходимости - через async-загрузку модулей у вебпака.

Хотя я тоже был когда-то адептом спрайтов.

Не знаю насчёт лучшего способа, но для себя я нашёл такой способ.

Способ позволяет менять РАЗМЕР иконки и любые другие цвета и свойства (надо для них CSS Custom properties написать, это несложно).
Файл SVG:


<svg xmlns="http://www.w3.org/2000/svg"
     viewBox="0 0 100 100">
    <!-- Delete width and height of <svg> tag, only viewBox is needed -->
    <!-- If you specify there fill or stroke, they will be default -->

    <!-- DO NOT PUT COMMENTS ABOVE <svg> TAG - it creates extra element-->

    <!-- USING CSS VARIABLES -->
    <style>
        :root {
            --first-circle-color: red;
            --second-circle-color: black;
            --third-circle-color: green;
            --rect-color: blue;
        }
    </style>


    <!--
        Delete all fill and stroke in children
        in order them to take fill and stroke from parent <svg> tag.
        Set stroke="none" or fill="none" if you want to disable them.
        (!!! if you not set them, then they can draw with defaults)
    -->
    <rect x="16" y="57" width="68" height="16" stroke="none" fill="var(--rect-color)" />
    <circle cx="25.5" cy="40.5" r="9.5" stroke="none" fill="var(--first-circle-color)" />
    <circle cx="50.5" cy="40.5" r="9.5" stroke="none" fill="var(--second-circle-color)" />
    <circle cx="74.5" cy="40.5" r="9.5" stroke="none" fill="var(--third-circle-color)" />

</svg>

Файл компоненты React:

import React, {useLayoutEffect, useRef} from 'react'
import { ReactComponent as SomeSvgSvg } from 'src/assets/icons/some-svg-4.svg'

// USING CSS VARIABLES

const SomeSvgIc4 = (
    {color1, color2, color3, color4, size}
        : {color1:string, color2:string, color3:string, color4:string, size?:number}) => {

    const svgRef = useRef<SVGSVGElement>(null)
    useLayoutEffect(()=>{
        const svg = svgRef.current
        if (svg) {
            svg.style.setProperty('--first-circle-color', color1)
            svg.style.setProperty('--second-circle-color', color2)
            svg.style.setProperty('--third-circle-color', color3)
            svg.style.setProperty('--rect-color', color4)
        }
    },[color1,color2,color3,color4])

    return <SomeSvgSvg ref={svgRef}
        style={{ width: size, height: size, maxWidth: '100%', maxHeight: '100%' }}
    />
}
export default SomeSvgIc4

Использование:

{/* ... */}
<div style={{height:200, width:200, background: 'yellow'}}>
	<SomeSvgIc4 color1={"gold"} color2={"red"} color3={"blue"} color4={"green"} />
</div>
<div>
	<SomeSvgIc4 
		color1={"gold"} color2={"red"} color3={"blue"} color4={"green"} 
		size={200}
	/>
</div>
{/* ... */}

Код взял из своего тестового реакт проекта, где иногда тестирую фичи реакта

https://github.com/RainFourth/react-ts-test/blob/main/src/components/SvgIcons/SomeSvgIc4.tsx

Спасибо за статью!
Я бы к минусам спрайта еще бы отнес то, что даже если мы отрендерили из него какие-то иконки, а затем у нас пропал интернет, и например появляется уведомление о неудачном действии (из за пропавшего интернета) - если в этом новом элементе (плашке уведомления) будет иконка из спрайта запрошенная через тег <use> - у нас отрисуется битая картинка, как будто отправляется вновь http запрос. Поэтому я чаще использую ReactComponent, а то что растет DOM - мы в реакте с ним и не работаем, а современные браузеры работают достаточно оптимизированно, мне кажется такие вещи они секут и не парсят каждую из 20ти блоков одинакого svg заново, но могу ошибаться. Вообщем для обычных задач ReactComponent пока что достаточен

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории