Los desarrolladores suelen insertar SVG directamente en JSX. Esto es cómodo de usar, pero aumenta el tamaño del paquete JS. En busca de la optimización, decidí buscar otra forma de usar íconos SVG sin saturar el paquete. Hablaremos sobre los sprites SVG, qué son, cómo usarlos y qué herramientas están disponibles para trabajar con ellos. Comenzando con la teoría, escribiremos un script que genere un objeto SVG paso a paso y concluiremos analizando los complementos para y . vite webpack ¿Qué es SVG Sprite? Un objeto de imagen es una colección de imágenes colocadas en una sola imagen. A su vez, SVG Sprite es una colección de contenido SVG, empaquetado en , que se coloca en . <symbol /> <svg /> Por ejemplo, tenemos un icono de lápiz SVG simple: Para obtener un objeto SVG, reemplazaremos la etiqueta con y la envolveremos con externamente: <svg /> <symbol /> <svg /> Ahora es un objeto SVG y tenemos un ícono dentro con . id="icon-pen" Ok, pero deberíamos descubrir cómo colocar este ícono en nuestra página HTML. Usaremos la etiqueta con el atributo , especificando el ID del icono, y duplicará este elemento dentro de SVG. <use /> href Echemos un vistazo a un ejemplo de cómo funciona : <use /> En este ejemplo, hay dos círculos. El primero tiene un contorno azul y el segundo es un duplicado del primero pero con un relleno rojo. Volvamos a nuestro objeto SVG. Junto con el uso de , obtendremos esto: <use /> Aquí tenemos un botón con el icono de nuestro bolígrafo. Hasta ahora, hemos usado un ícono sin su código en . Si tenemos más de un botón en la página, no afectará más de una vez el tamaño de nuestro diseño HTML porque todos los íconos provendrán de nuestro objeto SVG y serán reutilizables. <button /> Creando un archivo SVG Sprite Movamos nuestro objeto SVG a un archivo separado para no tener que saturar el archivo . Primero, cree un archivo y coloque un objeto SVG en él. El siguiente paso es proporcionar acceso al ícono usando el atributo en : index.html sprite.svg href <use/> Automatización de la creación de sprites SVG Para ahorrar mucho tiempo en el uso de íconos, configuremos una automatización para este proceso. Para acceder fácilmente a los iconos y gestionarlos como queramos, es necesario separarlos, cada uno en su propio archivo. Primero, deberíamos poner todos los íconos en la misma carpeta, por ejemplo: Ahora, escribamos un script que tome estos archivos y los combine en un único objeto SVG. Cree el archivo en el directorio raíz de su proyecto. generateSvgSprite.ts Instalar la biblioteca : global npm i -D glob Obtenga una variedad de rutas completas para cada ícono usando : globSync Ahora, iteraremos cada ruta de archivo y obtendremos el contenido del archivo utilizando la biblioteca integrada de Node: fs Genial, tenemos el código SVG de cada ícono y ahora podemos combinarlos, pero debemos reemplazar la etiqueta dentro de cada ícono con la etiqueta y eliminar los atributos SVG inútiles. svg symbol Deberíamos analizar nuestro código SVG con alguna biblioteca de análisis HTML para obtener su representación DOM. Usaré : node-html-parser Hemos analizado el código SVG y obtenido el elemento SVG como si fuera un elemento HTML real. Usando el mismo analizador, cree un elemento vacío para mover elementos secundarios de al : symbol svgElement symbol Después de extraer elementos secundarios de , también deberíamos obtener los atributos y . Como , establezcamos el nombre del archivo del icono. svgElement id viewBox id Ahora tenemos un elemento que se puede colocar en un objeto SVG. Entonces, simplemente defina la variable antes de iterar los archivos, transforme el en una cadena y empújelo en : symbol symbols symbolElement symbols El último paso es crear el objeto SVG. Representa una cadena con en “raíz” y símbolos como hijos: svg const svgSprite = `<svg>${symbols.join('')}</svg>`; Y si no estás pensando en usar complementos, de los que hablaré a continuación, debes colocar el archivo con el sprite creado en alguna carpeta estática. La mayoría de los paquetes usan una carpeta : public fs.writeFileSync('public/sprite.svg', svgSprite); Y esto es todo; el script está listo para usar: // 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); Puedes poner este script en la raíz de tu proyecto y ejecutarlo con : tsx npx tsx generateSvgSprite.ts En realidad, estoy usando aquí porque solía escribir código en TypeScript en todas partes y esta biblioteca le permite ejecutar scripts de nodo escritos en TypeScript. Si desea utilizar JavaScript puro, puede ejecutarlo con: tsx node generateSvgSprite.js Entonces, resumamos lo que hace el script: Busca en la carpeta cualquier archivo . src/icons .svg Extrae el contenido de cada icono y crea un elemento de símbolo a partir de él. Envuelve todos los símbolos en un solo <svg />. Crea el archivo en la carpeta . sprite.svg public Cómo cambiar los colores de los iconos Cubramos un caso frecuente e importante: ¡los colores! Creamos un script donde el ícono va dentro de un objeto, pero este ícono puede tener diferentes colores a lo largo del proyecto. Debemos tener en cuenta que no sólo los elementos pueden tener atributos de relleno o trazo, sino también , , y otros. Hay una característica CSS muy útil que nos ayudará: . <svg/> path circle line currentcolor Esta palabra clave representa el valor de la propiedad de color de un elemento. Por ejemplo, si usamos el en un elemento que tiene un , entonces este elemento tendrá un fondo rojo. color: red background: currentcolor Básicamente, necesitamos cambiar cada valor de atributo de trazo o relleno al . Espero que no lo estés viendo hecho manualmente, je. E incluso escribir algún código que reemplace o analice cadenas SVG no es muy eficiente en comparación con una herramienta muy útil . currentcolor , svgo Este es un optimizador de SVG que puede ayudar no solo con los colores sino también con la eliminación de información redundante de SVG. Instalemos : svgo npm i -D svgo tiene complementos integrados y uno de ellos es , que tiene la propiedad . Si usamos esta salida SVG, reemplazará los colores con . Aquí está el uso de junto con : 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); Y la salida será: <svg viewBox="0 0 24 24"><path fill="currentColor" d="m15 5 4 4"/></svg> Agreguemos a nuestro script mágico que escribimos en la 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); Y ejecuta el script: npx tsx generateSvgSprite.ts Como resultado, el objeto SVG contendrá iconos con . Y estos íconos se pueden usar en todas partes del proyecto con el color que desee. currentColor Complementos Tenemos un script y podemos ejecutarlo cuando queramos, pero es un poco inconveniente que debamos usarlo manualmente. Por lo tanto, recomiendo algunos complementos que pueden ver nuestros archivos y generar sprites SVG sobre la marcha: .svg (para usuarios ) vite-plugin-svg-spritemap de vite Este es mi complemento que contiene básicamente este script que acabamos de crear en este artículo. El complemento tiene habilitado el reemplazo de forma predeterminada, por lo que puede configurar el complemento con bastante facilidad. currentColor // vite.config.ts import svgSpritemap from 'vite-plugin-svg-spritemap'; export default defineConfig({ plugins: [ svgSpritemap({ pattern: 'src/icons/*.svg', filename: 'sprite.svg', }), ], }); (para usuarios ) svg-spritemap-webpack-plugin de webpack Utilicé este complemento de Webpack hasta que cambié a Vite. Pero este complemento sigue siendo una buena solución si utiliza Webpack. Debes habilitar manualmente la conversión de color y se verá así: // 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 en diseño Proporcionaré un ejemplo en , pero puedes implementarlo donde quieras porque se trata principalmente de HTML. Entonces, como tenemos en nuestra carpeta de compilación, podemos acceder al archivo sprite y crear el 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" />; }; El resultado final Entonces, resumiendo todo, para evitar mucho trabajo manual con íconos, nosotros: Puede guardar y mantener fácilmente iconos organizados en el proyecto con nombres deseables. tener un script que combine todos los íconos en un solo objeto en un archivo separado que reduce el tamaño del paquete y nos permite usar estos íconos en cualquier parte del proyecto tener una herramienta útil que nos ayuda a mantener los íconos libres de atributos innecesarios y cambiar los colores en el lugar de uso tener un complemento que pueda ver nuestros archivos de íconos y generar sprites sobre la marcha como parte del proceso de construcción tener un componente Icon que sea una guinda Conclusión La eficiencia en el desarrollo no se trata sólo de ahorrar tiempo; se trata de desbloquear nuestro potencial creativo. Automatizar las tareas esenciales, como la gestión de iconos, no es sólo un atajo; es una puerta de entrada a una experiencia de codificación más fluida e impactante. Y al ahorrar tiempo en cosas tan rutinarias, podrá concentrarse en tareas más complejas y crecer como desarrollador más rápido.