在 Vue 项目中更优雅地使用 icon

1 前言

在 Web 开发中,我们经常会用到 icon,icon 的使用经历了从图片到字体,再到 svg 的演变过程,也产生出相应的 icon 库,如雪碧图、Font AwesomeIconfont 等等。

随着前端的发展,icon 使用方案落在了 svg 上,svg 有着矢量图的优势,可以无限放大而不失真,而且 svg 本身就是一种 XML 文件,可以直接在 HTML 中使用,也可以通过 CSS 进行样式控制,但是在 Vue 项目中使用 svg 时,我们会遇到一些问题,本文将介绍如何在 Vue 项目中更优雅的使用 svg icon。

2 工具

svg-sprite-loader用来打包 svg 图标,svgo-loader 来精简我们的 svg 内容。

3 封装组件

src/components 目录下新建 SvgIcon.vue 组件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<template>
  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  <svg v-else class="svg-icon" aria-hidden="true" v-on="$listeners">
    <use :href="iconName" />
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  computed: {
    isExternal() {
      return /^(https?:\/\/|data:image)/.test(this.iconClass)
    },
    iconName() {
      return `#icon-${this.iconClass}`
    },
    styleExternalIcon() {
      return {
        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
      }
    }
  }
}
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover!important;
  display: inline-block;
}
</style>

4 安装

1
2
3
npm install svg-sprite-loader svgo-loader -D
# 或
yarn add svg-sprite-loader svgo-loader -D

5 配置

统一将所有的 icon 都以 svg 的形式都放在 src/assets/icons 目录中。

然后在 vue.config.js 中添加如下配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const { defineConfig } = require('@vue/cli-service')
const path = require('path')

function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = defineConfig({
  // ...
  chainWebpack: (config) => {
    // set svg-sprite-loader
    const svgPath = resolve('src/assets/icons')
    config.module
      .rule('svg')
      .exclude.add(svgPath)
      .end()
    config.module
      .rule('svg-icon')
      .test(/.svg$/)
      .include.add(svgPath)
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]',
      })
      .end()
      // remove origin svg fill attr
      .use('svgo-loader')
      .loader('svgo-loader')
      .tap((options) => ({
        ...options,
        // 删除 svg 中 fill 和 fill-rule
        plugins: [{ name: 'removeAttrs', params: { attrs: 'fill|fill-rule' } }],
      }))
      .end()
  },
  // ...
})

6 自动引入

自动引入 src/assets/icons 中的 icon,需要用到 webpack 中的 require.context

src/main.js 中引入所有的 svg 图标,之后可在文件夹自行添加或者删除图标,所以图标都会被自动导入,无需手动操作:

1
2
3
4
5
6
7
8
9
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'

// register svg component globally 
Vue.component('SvgIcon', SvgIcon)
// require all svg
const requireAll = (requireContext) => requireContext.keys().map(requireContext)
const req = require.context('@/assets/icons', false, /\.svg$/)
requireAll(req)

7 使用 icon

1
<svg-icon icon-class="fullscreen"  class='custom-class' />

7.1 颜色

svg-icon 默认会读取其父级的 color fill: currentColor;

你可以改变父级的 color 或者直接改变 fill 的颜色即可。

7.2 大小

图标可从 iconfont 项目中下载或者由 UI 切图,同一个项目中使用的 Svg Icon 图标建议统一大小规格,比如 128*128

8 示例

本文 示例 代码已上传至 vue-el-demo 项目中,可自行下载查看。

9 参考资料

相关内容

Buy me a coffee~
Lruihao 支付宝支付宝
Lruihao 微信微信
0%