Un sistema de iconos SVG similar a una fuente para Vue
Administrar una colección personalizada de íconos en una aplicación Vue puede ser un desafío a veces. Una fuente de ícono es fácil de usar, pero para personalizarla, debe confiar en generadores de fuentes de terceros, y los conflictos de fusión pueden ser difíciles de resolver ya que las fuentes son archivos binarios.
En su lugar, usar archivos SVG puede eliminar esos puntos débiles, pero ¿cómo podemos asegurarnos de que sean iguales de fáciles de usar y al mismo tiempo facilitar agregar o eliminar íconos?
Así es como se ve mi sistema de iconos ideal:
- Para agregar íconos, simplemente colóquelos en una
icons
carpeta designada. Si ya no necesitas un ícono, simplemente eliminalo. - Para utilizar el ícono rocket.svg en una plantilla, la sintaxis es tan simple como
svg-icon icon="rocket" /
. - Los íconos se pueden escalar y colorear usando CSS
font-size
ycolor
propiedades (como una fuente de ícono). - Si aparecen varias instancias del mismo ícono en la página, el código SVG no se duplica cada vez.
- No se requiere edición de configuración del paquete web.
Esto es lo que construiremos escribiendo dos componentes pequeños de un solo archivo. Hay algunos requisitos específicos para esta implementación, aunque estoy seguro de que muchos de ustedes, magos, podrían reelaborar este sistema para otros marcos y herramientas de compilación:
- webpack: si usamos Vue CLI para desarrollar su aplicación, entonces ya está usando webpack.
- svg-inline-loader: esto nos permite cargar todo nuestro código SVG y limpiar las partes que no queremos. Continúe y corra
npm install svg-inline-loader --save-dev
desde la terminal para comenzar.
El componente de sprites SVG
Para cumplir con nuestro requisito de no repetir el código SVG para cada instancia de un ícono en la página, necesitamos crear un “sprite” SVG. Si no has oído hablar de un objeto SVG antes, considéralo como un SVG oculto que alberga otros SVG. En cualquier lugar donde necesitemos mostrar un ícono, podemos copiarlo fuera del objeto haciendo referencia a la identificación del ícono dentro de una use
etiqueta como esta:
svguse xlink_href="#rocket" //svg
Ese pequeño fragmento de código es esencialmente cómo SvgIcon
funcionará nuestro componente, pero primero vamos a crear el SvgSprite
componente. Aquí está el SvgSprite.vue
archivo completo; Algunas cosas pueden parecer desalentadoras al principio, pero las desglosaré todas.
!-- SvgSprite.vue --template svg v-html="$options.svgSprite" //templatescriptconst svgContext = require.context( '!svg-inline-loader?' + 'removeTags=true' + // remove title tags, etc. 'removeSVGTagAttrs=true' + // enable removing attributes 'removingTagAttrs=fill' + // remove fill attributes '!@/assets/icons', // search this directory true, // search subdirectories /w+.svg$/i // only include SVG files)const symbols = svgContext.keys().map(path = { // get SVG file content const content = svgContext(path) // extract icon id from filename const id = path.replace(/^./(.*).w+$/, '$1') // replace svg tags with symbol tags and id attribute return content.replace('svg', `symboltoken interpolation"${id}"`).replace('svg', 'symbol')})export default { name: 'SvgSprite', svgSprite: symbols.join('n'), // concatenate all symbols into $options.svgSprite}/script
En la plantilla, nuestro único svg
elemento tiene su contenido vinculado a $options.svgSprite
. En caso de que no esté familiarizado con $options
él, contiene propiedades que están directamente adjuntas a nuestro componente Vue. Podríamos haberlo adjuntado svgSprite
a nuestro componente data
, pero realmente no necesitamos que Vue configure la reactividad para esto, ya que nuestro cargador SVG solo se ejecutará cuando se cree nuestra aplicación.
En nuestro script, usamos require.context
para recuperar todos nuestros archivos SVG y limpiarlos mientras lo hacemos. Invocamos svg-inline-loader
y le pasamos varios parámetros usando una sintaxis que es muy similar a los parámetros de cadena de consulta. Los he dividido en varias líneas para que sean más fáciles de entender.
const svgContext = require.context( '!svg-inline-loader?' + 'removeTags=true' + // remove title tags, etc. 'removeSVGTagAttrs=true' + // enable removing attributes 'removingTagAttrs=fill' + // remove fill attributes '!@/assets/icons', // search this directory true, // search subdirectories /w+.svg$/i // only include SVG files)
Básicamente, lo que estamos haciendo aquí es limpiar los archivos SVG que se encuentran en un directorio específico (/assets/icons
para que estén en buenas condiciones para usarlos en cualquier lugar donde los necesitemos.
El removeTags
parámetro elimina las etiquetas que no necesitamos para nuestros íconos, como title
y style
. Especialmente queremos eliminar title
etiquetas, ya que pueden generar información sobre herramientas no deseadas. Si desea conservar cualquier estilo codificado en sus íconos, agréguelo removingTags=title
como parámetro adicional para que solo title
se eliminen las etiquetas.
También le decimos a nuestro cargador que elimine fill
los atributos, para que podamos configurar nuestros propios fill
colores con CSS más adelante. Es posible que quieras conservar tus fill
colores. Si ese es el caso, simplemente elimine los parámetros removeSVGTagAttrs
y removingTagAttrs
.
El último parámetro del cargador es la ruta a nuestra carpeta de iconos SVG. Luego le proporcionamos require.context
dos parámetros más para que busque subdirectorios y solo cargue archivos SVG.
Para anidar todos nuestros elementos SVG dentro de nuestro objeto SVG, tenemos que convertirlos de svg
elementos en elementos SVG symbol
. Esto es tan sencillo como cambiar la etiqueta y darle a cada una una única id
, que extraemos del nombre del archivo.
const symbols = svgContext.keys().map(path = { // extract icon id from filename const id = path.replace(/^./(.*).w+$/, '$1') // get SVG file content const content = svgContext(path) // replace svg tags with symbol tags and id attribute return content.replace('svg', `symboltoken interpolation"${id}"`).replace('svg', 'symbol')})
¿Qué hacemos con este SvgSprite
componente? Lo colocamos en nuestra página antes de cualquier ícono que dependa de él. Recomiendo agregarlo al principio del App.vue
archivo.
!-- App.vue --template div svg-sprite /!-- ... --
El componente del icono
Ahora construimos el SvgIcon.vue
componente.
!-- SvgIcon.vue --template svg :class="{ 'icon-spin': spin }" use :xlink:href="`#${icon}`" / /svg/templatescriptexport default { name: 'SvgIcon', props: { icon: { type: String, required: true, }, spin: { type: Boolean, default: false, }, },}/scriptstylesvg.icon { fill: currentColor; height: 1em; margin-bottom: 0.125em; vertical-align: middle; width: 1em;}svg.icon-spin { animation: icon-spin 2s infinite linear;}@keyframes icon-spin { from { transform: rotate(0deg); } to { transform: rotate(359deg); }}/style
Este componente es mucho más simple. Como se mencionó anteriormente, aprovechamos la use
etiqueta para hacer referencia a una identificación dentro de nuestro objeto. Eso id
proviene del icon
apoyo de nuestro componente.
Agregué un spin
accesorio que alterna una .icon-spin
clase como una parte opcional de animación, en caso de que alguna vez lo necesitemos. Esto podría resultar útil, por ejemplo, para un icono giratorio de carga.
svg-icon v-if="isLoading" icon="spinner" spin /
Dependiendo de tus necesidades, es posible que desees agregar accesorios adicionales, como rotate
o flip
. Si lo desea, simplemente puede agregar las clases directamente al componente sin usar accesorios.
La mayor parte del contenido de nuestro componente es CSS. Además de la animación giratoria, la mayor parte de esto se usa para hacer que nuestro ícono SVG actúe más como una fuente de ícono ¹. Para alinear los íconos con la línea base del texto, descubrí que aplicar vertical-align: middle
, junto con un margen inferior de 0.125em
, funciona en la mayoría de los casos. También configuramos el fill
valor del atributo en currentColor
, lo que nos permite colorear el icono como si fuera texto.
p svg-icon icon="exclamation-circle" /!-- This icon will be 2em and red. -- Error!/p
¡Eso es todo! Si desea utilizar el componente de ícono en cualquier lugar de su aplicación sin tener que importarlo a cada componente que lo necesite, asegúrese de registrar el componente en su main.js
archivo:
// main.jsimport Vue from 'vue'import SvgIcon from '@/components/SvgIcon.vue'Vue.component('svg-icon', SvgIcon)// ...
Pensamientos finales
Aquí hay algunas ideas de mejoras, que omití intencionalmente para mantener esta solución accesible:
- Escala los íconos que tienen dimensiones no cuadradas para mantener sus proporciones.
- Inyecte el objeto SVG en la página sin necesidad de un componente adicional.
- Haga que funcione con vite, que es una herramienta de compilación nueva, rápida (y sin paquetes web) del creador de Vue, Evan You.
- Aproveche la API de composición de Vue 3.
Si desea probar rápidamente estos componentes, creó una aplicación de demostración basada en la plantilla vue-cli predeterminada. ¡Espero que esto te ayude a desarrollar una implementación que se ajuste a las necesidades de tu aplicación!
¹ Si se pregunta por qué usamos SVG cuando queremos que se como una fuente de ícono, consulte la publicación clásica que enfrenta a los dos entre sí.
Deja un comentario