Icon.vue

Nico Prat
nicooprat
Published in
3 min readFeb 20, 2019
Icon from Iconmonstr

Aujourd’hui le but du jeu est d’avoir un composant Vue à la fois flexible et complet pour charger des icônes en SVG :

  • Simplifie l’ajout de nouvelles icônes
  • Ne crée pas de requête HTTP supplémentaires
  • Optimise le code SVG
  • Conserve la flexibilité des composants Vue
  • S’adapte visuellement au contexte

👉 Voici un dépôt d’exemple créé avec Vue CLI : https://github.com/nicooprat/icon.vue

La première étape consiste à ajouter un loader Webpack qui permet d’importer des SVG et de les traiter comme des composants Vue avec le plugin vue-svg-loader(documentation), permettant aussi d’optimiser le code au passage.

Attention toutefois aux options de SVGO par défaut, notamment la suppression de l’attribut viewbox qui peut poser des problèmes en terme de responsive par exemple.

Une fois installé il suffit de configurer vue.config.js :

module.exports = {
chainWebpack: (config) => {
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
// Use inline SVG by default, add ?external to the URL if you want external loading
// See: https://vue-svg-loader.js.org/faq.html#how-to-use-both-inline-and-external-svgs
svgRule
.oneOf('external')
.resourceQuery(/external/)
.use('file-loader')
.loader('file-loader')
.options({
name: 'assets/[name].[hash:8].[ext]',
})
.end()
.end()
.oneOf('inline')
.use('vue-svg-loader')
.loader('vue-svg-loader')
.options({
svgo: {
// Options: https://github.com/svg/svgo/blob/master/.svgo.yml
plugins: [{ removeViewBox: false }, { prefixIds: true }, { removeXMLNS: true }]
}
})
},
}

Grâce au plugin babel-plugin-wildcard(documentation) l’import des icônes se fait simplement de cette manière :

import * as Icons from '../assets/icons'

Si ESlint n’apprécie pas, vous pouvez précéder l’import de cette ligne :

// eslint-disable-next-line import/no-unresolved, import/no-extraneous-dependencies

Il suffira par la suite d’ajouter des fichiers SVG dans ce dossier. Ce plugin Webpack peut poser des problèmes de mise en cache, le plus simple est donc d’ajouter ce script au package.json à utiliser en cas de problème :

"scripts": {
"clear:babel-cache": "rimraf -rf ./node_modules/.cache/babel-loader/*"
}

On peut ensuite inclure dynamiquement le code SVG de l’icône demandée :

<template>
<component :is="icon"/>
</template>

Et appeler notre icône ailleurs dans l’app de cette façon :

<Icon icon="help"/>

Bien sûr il est toujours possible, comme n’importe quel composant Vue, de transmettre (et potentiellement de surcharger) des attributs :

<Icon icon="help" class="red" width="16" height="16"/>

Il peut être utile d’ajouter ce style pour que l’icône soit de la même couleur que le texte courant (bien penser à l’attribut scoped) :

<style scoped>
path {
fill: currentColor;
}
</style>

On peut aussi ajouter la propriété install à notre composant Icon pour l’instancier globalement plus facilement :

export default {
...
install(Vue) {
Vue.component(this.name, this)
},
}

Il ne reste qu’à ajouter à notre main.js :

import Icon from '@/components/Icon.vue'
Vue.use(Icon)

Updates

  1. On peut aussi ajouter un validator pour la prop du component : commit
  2. J’ai fini par préfixer les icônes par icon- pour éviter les conflits avec les éléments HTML existants : commit, référence

--

--

Nico Prat
nicooprat

Développeur & designer front-end. Freelance & fondateur de @globetrotterio