nicooprat
Published in

nicooprat

Icon.vue

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

--

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Nico Prat

Nico Prat

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

More from Medium

Some Common ARIA Properties for Accessible Web UI Components

Release note of egjs (Q1 2022)

Divide and Conquer your components

How to accomplish “Voronoi/Delaunay” transition animation using d3 and react-spring