Importar SVGs como componentes Vue
La idea es poder utilizar cualquier archivo .svg
como un componente Vue más; para ello, necesitamos incluir un loader en nuestro proyecto, svg-to-vue-component
Primero, se agrega la dependencia:
- Usando npm:
npm install svg-to-vue-component
- Usando yarn:
yarn add svg-to-vue-component
Luego, de acuerdo al tipo de proyecto, debemos configurarlo. En la documentación se muestran algunos ejemplos:
Vue CLI
Es uno de los casos más comunes. La configuración se hace en el archivo vue.config.js
en la raíz del proyecto:
module.exports = {
chainWebpack(config) {
// Only convert .svg files that are imported by these files as Vue component
const FILE_RE = /\.(vue|js|ts|svg)$/
// Use vue-cli's default rule for svg in non .vue .js .ts files
config.module.rule('svg').issuer(file => !FILE_RE.test(file))
// Use our loader to handle svg imported by other files
config.module
.rule('svg-component')
.test(/\.svg$/)
.issuer(file => FILE_RE.test(file))
.use('vue')
.loader('vue-loader')
.end()
.use('svg-to-vue-component')
.loader('svg-to-vue-component/loader')
}
}
Saber
Saber es un generador de sitios estáticos que nos permite usar Vue (e incluso mezclado con Markdown). Éste sitio está construido usando Saber.
Para configurar svg-to-vue-component en un proyecto Saber, debemos usar webpack-chain, cómo se explica en la documentación de Saber.
Entonces, creamos un archivo /src/saber-node.js
con el siguiente contenido:
exports.chainWebpack = function(config) {
const FILE_RE = /\.(vue|md|js|ts|svg)$/
config.module.rule('svg').issuer(file => !FILE_RE.test(file))
config.module
.rule('svg-component')
.test(/\.svg$/)
.issuer(file => FILE_RE.test(file))
.use('vue')
.loader('vue-loader')
.end()
.use('svg-to-vue-component')
.loader('svg-to-vue-component/loader')
}
El contenido es el mismo, solamente incluí tambien en la expresión regular
FILE_RE
la extensión md, para poder usar el loader en archivos Markdown
Luego, ya se pueden importar los SVG. Un ejemplo adaptado de Mi CV
<template>
<span class="inline-flex items-center space-x-4">
<TelegramIcon class="w-6 h-6 text-blue-400" />
<a
class="inline-block transition duration-150 ease-in-out border-b border-transparent hover:border-white"
href="https://t.me/AgustinOrtu"
target="_blank"
>
AgustinOrtu
</a>
</span>
</template>
<script>
import TelegramIcon from '../src/assets/icons/telegram.svg'
export default {
components: { TelegramIcon },
}
</script>
El cual se renderiza así:
También es posible utilizar el componente dinámico de Vue, por ejemplo:
<template>
<div class="pb-4">
<div class="flex items-center space-x-4">
<button @click="icon = 'ExternalLinkIcon'" :class="icon == 'ExternalLinkIcon' ? 'bg-indigo-700' : 'bg-indigo-500'" class="px-4 py-2 text-sm leading-5 text-white uppercase hover:bg-indigo-700 focus:outline-none focus:shadow-outline-indigo" type="button">External Link</button>
<button @click="icon = 'TerminalIcon'" :class="icon == 'TerminalIcon' ? 'bg-indigo-700' : 'bg-indigo-500'" class="px-4 py-2 text-sm leading-5 text-white uppercase hover:bg-indigo-700 focus:outline-none focus:shadow-outline-indigo" type="button">Terminal</button>
<div class="flex items-center space-x-4">
<Component :is="icon" class="w-12 h-12 text-indigo-500" />
<span>{{ icon }}</span>
</div>
</div>
</div>
</template>
<script>
import ExternalLinkIcon from 'heroicons/solid/external-link.svg'
import TerminalIcon from 'heroicons/solid/terminal.svg'
export default {
components: { ExternalLinkIcon, TerminalIcon },
data() {
return {
icon: 'ExternalLinkIcon'
}
},
}
</script>
El resultado es el siguiente:
En este caso, estoy utilizando el set de iconos Heroicons diseñados por Steve Schoger