Os desenvolvedores costumam inserir SVG diretamente no JSX. Isso é conveniente de usar, mas aumenta o tamanho do pacote JS. Na busca pela otimização, decidi encontrar outra maneira de usar ícones SVG sem sobrecarregar o pacote. Falaremos sobre sprites SVG, o que são, como usá-los e quais ferramentas estão disponíveis para trabalhar com eles. Começando pela teoria, escreveremos um script que gera um sprite SVG passo a passo e concluiremos discutindo plugins para e . vite webpack O que é Sprite SVG? Um sprite de imagem é uma coleção de imagens colocadas em uma única imagem. Por sua vez, SVG Sprite é uma coleção de conteúdo SVG, agrupado em , que é colocado em . <symbol /> <svg /> Por exemplo, temos um ícone de caneta SVG simples: Para obter um sprite SVG, substituiremos a tag por e envolveremos externamente: <svg /> <symbol /> <svg /> Agora é um sprite SVG e temos um ícone dentro com . id="icon-pen" Ok, mas devemos descobrir como colocar esse ícone em nossa página HTML. Usaremos a tag com o atributo , especificando o ID do ícone, e duplicará este elemento dentro do SVG. <use /> href Vamos dar uma olhada em um exemplo de como funciona : <use /> Neste exemplo, existem dois círculos. O primeiro tem contorno azul e o segundo é uma duplicata do primeiro, mas com preenchimento vermelho. Voltemos ao nosso sprite SVG. Junto com o uso de , obteremos isto: <use /> Aqui temos um botão com nosso ícone de caneta. Até agora, usamos um ícone sem seu código em . Se tivermos mais de um botão na página, isso não afetará mais de uma vez o tamanho do nosso layout HTML, pois todos os ícones virão do nosso sprite SVG e serão reutilizáveis. <button /> Criando arquivo SVG Sprite Vamos mover nosso sprite SVG para um arquivo separado para que não tenhamos que sobrecarregar o arquivo . Primeiro, crie um arquivo e coloque um sprite SVG nele. A próxima etapa é fornecer acesso ao ícone usando o atributo em : index.html sprite.svg href <use/> Automatizando a criação de Sprites SVG Para economizar muito tempo no uso de ícones, vamos configurar uma automação para esse processo. Para ter fácil acesso aos ícones e gerenciá-los como quisermos, eles devem ser separados, cada um em seu arquivo. Primeiramente devemos colocar todos os ícones na mesma pasta, por exemplo: Agora, vamos escrever um script que capture esses arquivos e os combine em um único sprite SVG. Crie o arquivo no diretório raiz do seu projeto. generateSvgSprite.ts Instale a biblioteca : glob npm i -D glob Obtenha uma série de caminhos completos para cada ícone usando : globSync Agora, iremos iterar cada caminho de arquivo e obter o conteúdo do arquivo usando a biblioteca interna do Node : fs Ótimo, temos o código SVG de cada ícone e agora podemos combiná-los, mas devemos substituir a tag dentro de cada ícone pela tag de e remover atributos SVG inúteis. svg symbol Deveríamos analisar nosso código SVG com alguma biblioteca de analisador HTML para obter sua representação DOM. Usarei : node-html-parser Analisamos o código SVG e obtivemos o elemento SVG como se fosse um elemento HTML real. Usando o mesmo analisador, crie um elemento vazio para mover os filhos de para : symbol svgElement symbol Depois de extrair os filhos de , também devemos obter os atributos e dele. Como , vamos definir o nome do arquivo de ícone. svgElement id viewBox id Agora temos um elemento que pode ser colocado em um sprite SVG. Portanto, basta definir a variável antes de iterar os arquivos, transformar o em uma string e colocá-lo em : symbol symbols symbolElement symbols A etapa final é criar o próprio sprite SVG. Representa uma string com em “root” e símbolos como filhos: svg const svgSprite = `<svg>${symbols.join('')}</svg>`; E se você não está pensando em usar plugins, dos quais falarei a seguir, você precisa colocar o arquivo com o sprite criado em alguma pasta estática. A maioria dos bundlers usa uma pasta : public fs.writeFileSync('public/sprite.svg', svgSprite); E é isso; o script está pronto para uso: // 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); Você pode colocar este script na raiz do seu projeto e executá-lo com : tsx npx tsx generateSvgSprite.ts Na verdade, estou usando aqui porque costumava escrever código em TypeScript em qualquer lugar, e esta biblioteca permite executar scripts de nó escritos em TypeScript. Se quiser usar JavaScript puro, você pode executá-lo com: tsx node generateSvgSprite.js Então, vamos resumir o que o script está fazendo: Ele procura na pasta qualquer arquivo . src/icons .svg Extrai o conteúdo de cada ícone e cria um elemento de símbolo a partir dele. Ele reúne todos os símbolos em um único <svg />. Ele cria o arquivo na pasta . sprite.svg public Como alterar as cores dos ícones Vamos abordar um caso frequente e importante: as cores! Criamos um script onde o ícone entra em um sprite, mas esse ícone pode ter cores diferentes ao longo do projeto. Devemos ter em mente que não apenas os elementos podem ter atributos de preenchimento ou traço, mas também , , e outros. Há um recurso CSS muito útil que nos ajudará - . <svg/> path circle line currentcolor Esta palavra-chave representa o valor da propriedade color de um elemento. Por exemplo, se usarmos em um elemento que possui um , então este elemento terá um fundo vermelho. color: red background: currentcolor Basicamente, precisamos alterar cada valor de atributo de traço ou preenchimento para . Espero que você não esteja vendo isso feito manualmente, heh. E mesmo escrever algum código que substitua ou analise strings SVG não é muito eficiente em comparação com uma ferramenta muito útil . currentcolor svgo Este é um otimizador SVG que pode ajudar não apenas com cores, mas também na remoção de informações redundantes do SVG. Vamos instalar : o svgo npm i -D svgo possui plug-ins integrados, e um deles é , que possui a propriedade . Se usarmos esta saída SVG, ela substituirá cores por . Aqui está o uso de junto com : 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); E a saída será: <svg viewBox="0 0 24 24"><path fill="currentColor" d="m15 5 4 4"/></svg> Vamos adicionar ao nosso script mágico que escrevemos na parte anterior: 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); E execute o script: npx tsx generateSvgSprite.ts Como resultado, o sprite SVG conterá ícones com . E esses ícones podem ser usados em qualquer lugar do projeto com qualquer cor que você desejar. currentColor Plug-ins Temos um script e podemos executá-lo quando quisermos, mas é um pouco inconveniente usá-lo manualmente. Portanto, recomendo alguns plug-ins que podem visualizar nossos arquivos e gerar sprites SVG em qualquer lugar: .svg (para usuários ) vite-plugin-svg-spritemap vite Este é o meu plugin que contém basicamente este script que acabamos de criar neste artigo. O plugin tem a substituição habilitada por padrão, então você pode configurar o plugin com bastante facilidade. currentColor // vite.config.ts import svgSpritemap from 'vite-plugin-svg-spritemap'; export default defineConfig({ plugins: [ svgSpritemap({ pattern: 'src/icons/*.svg', filename: 'sprite.svg', }), ], }); (para usuários ) svg-spritemap-webpack-plugin do webpack Usei este plugin Webpack até mudar para o Vite. Mas este plugin ainda é uma boa solução se você estiver usando o Webpack. Você deve ativar manualmente a conversão de cores e ficará assim: // 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', }, }), ], } Uso em Layout Darei um exemplo em , mas você pode implementá-lo onde quiser porque se trata principalmente de HTML. Então, como temos em nossa pasta build, podemos acessar o arquivo sprite e criar o componente básico: React sprite.svg Icon const Icon: FC<{ name: string }> = ({ name }) => ( <svg> <use href={`/sprite.svg#${name}`} /> </svg> ); const App = () => { return <Icon name="pen" />; }; O resultado final Então, resumindo tudo, para evitar muito trabalho manual com ícones, nós: pode facilmente salvar e manter ícones organizados no projeto com nomes desejados temos um script que combina todos os ícones em um único sprite em um arquivo separado que reduz o tamanho do pacote e nos permite usar esses ícones em qualquer lugar do projeto temos uma ferramenta útil que nos ajuda a manter os ícones livres de atributos desnecessários e alterar as cores no local de uso temos um plugin que pode observar nossos arquivos de ícones e gerar sprites em movimento como parte do processo de construção tem um componente Icon que é uma cereja no topo Conclusão A eficiência no desenvolvimento não envolve apenas economia de tempo; trata-se de desbloquear nosso potencial criativo. Automatizar tarefas essenciais, como gerenciar ícones, não é apenas um atalho; é uma porta de entrada para uma experiência de codificação mais suave e impactante. E economizando tempo nessas tarefas rotineiras, você pode se concentrar em tarefas mais complexas e crescer como desenvolvedor com mais rapidez.