Разработчики часто вставляют SVG непосредственно в JSX. Это удобно в использовании, но увеличивает размер пакета JS. В поисках оптимизации я решил найти другой способ использования SVG-значков, не загромождая пакет. Мы поговорим о SVG-спрайтах, что это такое, как их использовать и какие инструменты доступны для работы с ними. Начав с теории, мы напишем скрипт, который шаг за шагом генерирует SVG-спрайт, и закончим обсуждением плагинов для и . Vite Webpack Что такое SVG-спрайт? Спрайт изображения — это набор изображений, помещенных в одно изображение. В свою очередь, SVG Sprite представляет собой коллекцию SVG-контента, завернутую в , который помещается в . <symbol /> <svg /> Например, у нас есть простой значок пера в формате SVG: Чтобы получить спрайт SVG, мы заменим тег на и обернем его внешне : <svg /> <symbol /> <svg /> Теперь это SVG-спрайт, и внутри у нас есть значок с . id="icon-pen" Хорошо, но нам нужно придумать, как разместить этот значок на нашей HTML-странице. Мы будем использовать тег с атрибутом , указывающим идентификатор значка, и он будет дублировать этот элемент внутри SVG. <use /> href Давайте посмотрим на пример того, как работает : <use /> В этом примере есть два круга. Первый имеет синий контур, а второй — дубликат первого, но с красной заливкой. Давайте вернемся к нашему SVG-спрайту. Вместе с использованием мы получим следующее: <use /> Здесь у нас есть кнопка со значком пера. До сих пор мы использовали значок без его кода в . Если у нас на странице более одной кнопки, это не повлияет на размер нашего HTML-макета более одного раза, поскольку все значки будут взяты из нашего спрайта SVG и их можно будет использовать повторно. <button /> Создание файла спрайта SVG Давайте переместим наш SVG-спрайт в отдельный файл, чтобы не загромождать файл . Сначала создайте файл и поместите в него спрайт SVG. Следующий шаг — предоставить доступ к значку с помощью атрибута в : index.html sprite.svg href <use/> Автоматизация создания SVG-спрайтов Чтобы сэкономить много времени на использовании иконок, давайте настроим автоматизацию этого процесса. Чтобы получить легкий доступ к иконкам и управлять ими так, как нам хочется, их приходится разделить, каждую в отдельный файл. Во-первых, нам следует поместить все значки в одну папку, например: Теперь давайте напишем скрипт, который захватывает эти файлы и объединяет их в один спрайт SVG. Создайте файл в корневом каталоге вашего проекта. generateSvgSprite.ts Установите библиотеку : glob npm i -D glob Получите массив полных путей для каждого значка с помощью : globSync Теперь мы пройдемся по каждому пути к файлу и получим содержимое файла, используя встроенную библиотеку Node : fs Отлично, у нас есть SVG-код каждой иконки, и теперь мы можем их объединить, но нам нужно заменить тег внутри каждой иконки тегом и удалить ненужные атрибуты SVG. svg symbol Нам следует проанализировать наш код SVG с помощью какой-нибудь библиотеки анализатора HTML, чтобы получить его представление DOM. Я буду использовать : node-html-parser Мы проанализировали код SVG и получили элемент SVG, как если бы это был настоящий элемент HTML. Используя тот же парсер, создайте пустой элемент , чтобы переместить дочерние элементы в : symbol svgElement symbol После извлечения дочерних элементов из мы также должны получить из него атрибуты и . В качестве зададим имя файла значка. svgElement id viewBox id Теперь у нас есть элемент , который можно поместить в спрайт SVG. Итак, просто определите переменную перед итерацией файлов, преобразуйте в строку и поместите ее в : symbol symbols symbolElement symbols Последний шаг — создание самого спрайта SVG. Он представляет собой строку с в «корне» и символами в качестве дочерних элементов: svg const svgSprite = `<svg>${symbols.join('')}</svg>`; А если вы не планируете использовать плагины, о которых я расскажу ниже, вам нужно поместить файл с созданным спрайтом в какую-нибудь статическую папку. Большинство сборщиков используют папку: public fs.writeFileSync('public/sprite.svg', svgSprite); И вот оно; скрипт готов к использованию: // generateSvgSprite.ts import { globSync } from 'glob'; import fs from 'fs'; import { HTMLElement, parse } from 'node-html-parser'; import path from 'path'; const svgFiles = globSync('src/icons/*.svg'); const symbols: string[] = []; svgFiles.forEach(file => { const code = fs.readFileSync(file, 'utf-8'); const svgElement = parse(code).querySelector('svg') as HTMLElement; const symbolElement = parse('<symbol/>').querySelector('symbol') as HTMLElement; const fileName = path.basename(file, '.svg'); svgElement.childNodes.forEach(child => symbolElement.appendChild(child)); symbolElement.setAttribute('id', fileName); if (svgElement.attributes.viewBox) { symbolElement.setAttribute('viewBox', svgElement.attributes.viewBox); } symbols.push(symbolElement.toString()); }); const svgSprite = `<svg>${symbols.join('')}</svg>`; fs.writeFileSync('public/sprite.svg', svgSprite); Вы можете поместить этот скрипт в корень вашего проекта и запустить его с помощью : tsx npx tsx generateSvgSprite.ts На самом деле, я использую здесь , потому что раньше я везде писал код на TypeScript, а эта библиотека позволяет выполнять скрипты узлов, написанные на TypeScript. Если вы хотите использовать чистый JavaScript, вы можете запустить его с помощью: tsx node generateSvgSprite.js Итак, подведем итоги того, что делает скрипт: Он просматривает папку на наличие любых файлов . src/icons .svg Он извлекает содержимое каждого значка и создает из него элемент символа. Он объединяет все символы в один <svg />. Он создает файл в папке. sprite.svg public Как изменить цвета значков Давайте рассмотрим один частый и важный случай: цвета! Мы создали скрипт, в котором иконка переходит в спрайт, но на протяжении всего проекта эта иконка может иметь разные цвета. Следует иметь в виду, что не только элементы могут иметь атрибуты заливки или обводки, но также , , и другие. Есть очень полезная функция CSS, которая нам поможет — . <svg/> path circle line currentcolor Это ключевое слово представляет значение свойства цвета элемента. Например, если мы используем для элемента, имеющего , то этот элемент будет иметь красный фон. color: red background: currentcolor По сути, нам нужно изменить значение каждого атрибута штриха или заливки на . Надеюсь, вы не видите, что это делается вручную, хех. И даже написание кода, который будет заменять или анализировать строки SVG, не очень эффективно по сравнению с очень полезным инструментом . currentcolor svgo Это оптимизатор SVG, который может помочь не только с цветами, но и с удалением избыточной информации из SVG. Давайте установим : svgo npm i -D svgo имеет встроенные плагины, и один из них — , у которого есть свойство . Если мы используем этот вывод SVG, он заменит цвета на . Вот использование вместе с : svgo convertColors currentColor: true currentcolor svgo convertColors import { optimize } from 'svgo'; const output = optimize( '<svg viewBox="0 0 24 24"><path fill="#000" d="m15 5 4 4" /></svg>', { plugins: [ { name: 'convertColors', params: { currentColor: true, }, } ], } ) console.log(output); И вывод будет: <svg viewBox="0 0 24 24"><path fill="currentColor" d="m15 5 4 4"/></svg> Давайте добавим в наш волшебный скрипт, который мы написали в предыдущей части: svgo // generateSvgSprite.ts import { globSync } from 'glob'; import fs from 'fs'; import { HTMLElement, parse } from 'node-html-parser'; import path from 'path'; import { Config as SVGOConfig, optimize } from 'svgo'; // import `optimize` function const svgoConfig: SVGOConfig = { plugins: [ { name: 'convertColors', params: { currentColor: true, }, } ], }; const svgFiles = globSync('src/icons/*.svg'); const symbols: string[] = []; svgFiles.forEach(file => { const code = fs.readFileSync(file, 'utf-8'); const result = optimize(code, svgoConfig).data; // here goes `svgo` magic with optimization const svgElement = parse(result).querySelector('svg') as HTMLElement; const symbolElement = parse('<symbol/>').querySelector('symbol') as HTMLElement; const fileName = path.basename(file, '.svg'); svgElement.childNodes.forEach(child => symbolElement.appendChild(child)); symbolElement.setAttribute('id', fileName); if (svgElement.attributes.viewBox) { symbolElement.setAttribute('viewBox', svgElement.attributes.viewBox); } symbols.push(symbolElement.toString()); }); const svgSprite = `<svg xmlns="http://www.w3.org/2000/svg">${symbols.join('')}</svg>`; fs.writeFileSync('public/sprite.svg', svgSprite); И запустите скрипт: npx tsx generateSvgSprite.ts В результате спрайт SVG будет содержать значки с . И эти значки можно использовать везде в проекте, любого цвета. currentColor Плагины У нас есть скрипт, и мы можем запустить его, когда захотим, но немного неудобно использовать его вручную. Итак, я рекомендую несколько плагинов, которые могут просматривать наши файлы и генерировать SVG-спрайты на ходу: .svg (для пользователей ) vite-plugin-svg-spritemap vite Это мой плагин, который содержит в основном скрипт, который мы только что создали в этой статье. По умолчанию в плагине включена замена , поэтому вы можете легко его настроить. currentColor // vite.config.ts import svgSpritemap from 'vite-plugin-svg-spritemap'; export default defineConfig({ plugins: [ svgSpritemap({ pattern: 'src/icons/*.svg', filename: 'sprite.svg', }), ], }); (для пользователей ) svg-spritemap-webpack-plugin веб-пакета Я использовал этот плагин Webpack, пока не перешел на Vite. Но этот плагин по-прежнему является хорошим решением, если вы используете Webpack. Вам следует вручную включить преобразование цветов, и это будет выглядеть так: // webpack.config.js const SVGSpritemapPlugin = require('svg-spritemap-webpack-plugin'); module.exports = { plugins: [ new SVGSpritemapPlugin('src/icons/*.svg', { output: { svgo: { plugins: [ { name: 'convertColors', params: { currentColor: true, }, }, ], }, filename: 'sprite.svg', }, }), ], } Использование в макете Я приведу пример на , но вы можете реализовать его там, где захотите, потому что в основном он касается HTML. Итак, поскольку у нас есть в папке сборки, мы можем получить доступ к файлу спрайта и создать базовый компонент : React sprite.svg Icon const Icon: FC<{ name: string }> = ({ name }) => ( <svg> <use href={`/sprite.svg#${name}`} /> </svg> ); const App = () => { return <Icon name="pen" />; }; Окончательный результат Итак, резюмируя все, чтобы избежать большого количества ручной работы с иконками, мы: можно легко сохранять и систематизировать значки в проекте с желаемыми именами. иметь скрипт, который объединяет все значки в один спрайт в отдельном файле, что уменьшает размер пакета и позволяет нам использовать эти значки в любом месте проекта. есть полезный инструмент, который помогает нам очищать значки от ненужных атрибутов и менять цвета по месту использования. иметь плагин, который может просматривать наши файлы значков и генерировать спрайты на ходу в рамках процесса сборки. иметь компонент Icon, который является вишенкой на торте Заключение Эффективность разработки — это не только экономия времени; речь идет о раскрытии нашего творческого потенциала. Автоматизация таких мельчайших задач, как управление значками, — это не просто ярлык; это путь к более плавному и эффективному программированию. А сэкономив время на таких рутинных вещах, вы сможете сосредоточиться на более сложных задачах и быстрее расти как разработчик.