@antimodern

Лучший способ сделать систему SVG иконок?

Собственно, собрался навести порядок, и сделать нечто типо

<app-icon name="heart" />

Способов как это сделать уйма (я насчитал 7 штук). Остановился на 2 (с перспективой добавить анимацию и тд):

1. Тупо в лоб:
<template>
  <div>
    <svg v-if="name ==='heart'">
      ...
    </svg>
    <svg v-else-if="name ==='like'">
      ...
    </svg>
    ...
  </div>
</template>

ВОПРОС: будет ли дубликация объема кода загружаемого на клиент (используя стандартный стартер вебпак, например)? Т.е. один и тот же очень объемный компонент (скажем, в одном компоненте 200 иконок с v-if) используется много раз в приложении. Все будет заново загружатся, или вебпак как-то оптимизирует это?

2. Использовать спрайты: сделать один компонент-спрайт с символами, загружать его в корне проекта единоразово, и сделать компоннет Icon.vue в котором те же v-if только использующие use для спрайта.

3. Ваш вариант и почему так? (не надо мне кидать ссылку на Сару Драснер, мне этот способ не нравится)
  • Вопрос задан
  • 683 просмотра
Решения вопроса 1
delphinpro
@delphinpro
frontend developer
По-моему, оптимальный вариант тот, что повсеместно используется — каждая иконка отдельным компонентом.
<app-icon-heart/>
<app-icon-like/>


Спрайты здесь вряд ли нужны. Код и так собирается в единый бандл.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@nvdfxx
Senior Pomidor developer
<template>
    <svg :src=`../assets/icons/${name}.svg`></svg>
</template>

<script>
export default {
    props: {
        name: {
            type: String,
            required: true
        }
    }
}
</script>


И вызывать этот компонент например так <v-icon name="like"/>, вебпак, я думаю, с такой задачей вполне справится
Ответ написан
@dest86
svg-sprite-loader

Настраиваем лоадер
// vue.config.js
module.exports = {
   ...

    chainWebpack: config => {
      const Sprite = config.module.rule('svg-sprite')
    
      Sprite
        .test(/\.(svg)(\?.*)?$/)
        .include
         // указываем путь к папке с иконками что бы не было конфликтов(например со шрифтами)
          .add(path.resolve(__dirname,'src/assets/icons'))
        .end()                   
        .use('file-loader')
          .loader('file-loader')
          .options( {
            name: 'static/img/[name].[hash:8].[ext]'
          })              
        
      Sprite
        .use('file-loader')
        .loader('svg-sprite-loader')
  
    }
}


Создаем компонент для иконок
<template>
  <svg :class="className" xmlns="http://www.w3.org/2000/svg" :style="style" v-on="$listeners">
    <use :xlink:href="`#${name}`" xmlns:xlink="http://www.w3.org/1999/xlink"/>
  </svg>
</template>

<script>
export default {
  name: 'svg-icon',

  props: {
    name: {
      type: String,
      required: true
    },
    width: {
      type: Number,  
    },
    height: {
      type: Number,   
    }
  },
  mounted() {
    require(`@/assets/icons/${this.name}.svg`).default.url;
    
  },
  updated() {
    // у меня возникали проблемы с тем что иконки не всегда отображаются, поэтому продублировал запрос к иконке  
    require(`@/assets/icons/${this.name}.svg`).default.url;
  },
  computed: {

    className() {
      return 'svg-icon svg-icon--' + this.name;
    },
    style() {
      let style = {}
      if (this.width) {
        style.width = this.width+'px';
      }
      if (this.height) {
        style.height = this.height+'px';
      }
      return style;       
    }

  }
};
</script>

<style>
  .svg-icon {
    fill: currentColor;
    height: 24px;
    width: 24px;
    stroke: none;
  }
</style>


вызов иконки
<svgicon :name="`logo`" />

Соответственно компонент svgicon либо подключаем в каждом компоненте или глобально

//main.js
//....
import SvgIcon from '@/components/SvgIcon

//....
Vue.use(SvgIcon, {
  tagName: 'svgicon'
})
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы