iconfont阿里矢量图标svg组件Vue3实现

176 阅读1分钟

背景介绍

项目中我们经常遇到使用图标的场景,而大家经常用到的也就是阿里的iconfont字体库,以前我的使用习惯是下载到本地,解压后得到一个下图的文件夹

image.png

然后复制iconfont.ttf文件到项目中,我是放在和组件同级目录下

image.png

然后再将解压文件夹中的iconfont.css中的样式复制到组件中,如下

image.png

这样做的弊端就是添加新的图标后,要重新下载字体文件,并重复上边的操作流程。

SVG方式使用图标

iconfont下载字体图标时,如下图,选择下载按钮 image.png

选择复制SVG代码

image.png

复制SVG代码后,在 icons/svg 文件夹下创建自定义文件名的svg文件,我是在项目中创建icons文件夹,用于存放svg文件

image.png

这样以后图标有修改,或者加入新图标时,只需复制svg代码覆盖原文件中的代码或者创建新的svg文件即可了。

组件化实现(Vue3 Setup)

  • icons 文件夹下创建 index.js 文件,用来加载 icons/svg 文件下下的所有svg文件
// index.js

const request = require.context('./svg', false, /\.svg$/)
request.keys().forEach(request)
  • main.js 中引入 icons/index.js 文件

image.png

  • 加载svg文件我们还需要用到svg-sprite-loader,通过npm i svg-sprite-loader -D安装到项目中,

image.png

简单描述一下svg-sprite-loader的原理: svg-sprite-loader将svg文件内容放到body下,然后再我们使用的地方通过<use>进行复用

image.png

  • vue.config.js 中添加如下规则:
const { defineConfig } = require('@vue/cli-service')
const path = require('path')

// 定义resolve方法,获取绝对路径
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  devServer: {
    open: true
  },
  chainWebpack: config => {
    // 配置svg默认规则排除icons目录中svg文件处理
    config.module
        .rule('svg')
        .exclude.add(resolve('src/icons'))
        .end()

    // 新增icons规则,设置svg-sprite-loader处理icons目录中svg文件
    config.module
        .rule('icons')
        .test(/.svg$/)
        .include.add(resolve('src/icons'))
        .end()
        .use('svg-sprite-loader')
        .loader('svg-sprite-loader')
        .options({ symbolId: 'icon-[name]' })
        .end()
  }
})
  • 创建 SvgIcon.vue 文件
<template>
  <svg :class="svgClass" aria-hidden="true">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script setup>
import { defineProps, computed } from "vue";

const props = defineProps({
  iconClass: {
    type: String,
    default: ''
  },
  className: {
    type: String,
    default: ''
  }
})

const iconName = computed(() => {
  return `#icon-${props.iconClass}`
})
const svgClass = computed(() => {
  if (props.className) {
    return 'svg-icon ' + props.className
  } else {
    return 'svg-icon'
  }
})
</script>

<style scoped>
  .svg-icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
  }
</style>
  • 最后就能直接使用啦,代码和效果如下
<template>
  <div class="icon-wrap">
    <svg-icon icon-class="star-red" class-name="star" />
  </div>
</template>

<script setup>
</script>

<style scoped>
.icon-wrap {
  width: 100%;
  height: 600px;
  line-height: 600px;
  text-align: center;
}
.star {
  width: 100px !important;
  height: 100px !important;
}
</style>

image.png

写在最后

这个方案也是很久之前看到的一篇博客中学到的,现当做笔记写下来,同时也分享给大家,如发现有雷同之处,完全是站在巨人的肩膀上前进。