jrainlau / blog-articles

My personal blog.
https://jrain.vercel.app
40 stars 11 forks source link

Vue3 搭配 Vite 实现 SVG 图标组件也太香了吧 #34

Closed jrainlau closed 2 years ago

jrainlau commented 2 years ago

市面上几乎所有的组件库都会有其独特的图标集,相对应的也有着属于自己的一套图标组件实现方式。一般情况下,其图标会以 icon-font 的形式提供,如 element-ui,也有部分会使用 svg,如 ant-design。在这篇文章内我们不讨论 icon-font 和 svg 的优劣,只讨论如何利用 Vue3 搭配 Vite 来实现基于 svg 的图标组件。

一、项目准备

在全局安装好了 Vite 以后,我们首先初始化一个项目:

yarn create vite

# 选择 vue 框架,并使用普通的 vue 写法(你要用 vue-ts 也行)
✔ Project name: … svg-icon
✔ Select a framework: › vue
✔ Select a variant: › vue

这样就能初始化一个 Vue3 + Vite 的项目了,目录结构如下:

├── README.md
├── index.html
├── package.json
├── public
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   └── main.js
└── vite.config.js

接下来我们随便找几个 svg 图标,比如开源的 ionicons,我下载并把它们放在 /src/assets 目录底下。

image

项目搭建好了,图标素材也准备好了,下面开始编写通用的图标组件。

二、图标组件

/src/components 目录下新建一个 Icon.vue

<template>
  <i class="my-icon"></i>
</template>

一般来说,图标组件常用 <i></i> 标签来承载,我们这里也不例外。新建好了图标的 Vue 组件以后,就要开始思考我们应该如何把 SVG 图标给填充上去。

有人可能会说,直接用 <image /> 标签以图片 src 的形式引入 SVG 图标不就好了吗?这样没问题,但这样的方式有一个致命的缺陷,就是难以改变图标的颜色。作为一个通用的图标组件,改变其颜色和大小是非常常见的操作需求,因此我们不能走这条路。

熟悉 Webpack 的同学应该知道,Webpack 的生态系统里有着千奇百怪的 loader,几乎可以把任意格式的资源加载进来甚至直接变成 Vue 组件,那么在 Vite 生态下有没有呢?经过一番查找,我找到了一个名为 vite-svg-loader 的东西,恰好可以满足”加载 SVG 资源并把它变成 Vue 组件“的场景,马上试一试!

打开项目中的 vite.config.js,引入 vite-svg-loader

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import svgLoader from 'vite-svg-loader'

export default defineConfig({
  plugins: [vue(), svgLoader()]
})

接着在 Icon.vue 中直接 import 一个 SVG 图标:

<template>
  <i class="my-icon">
    <icon />
  </i>
</template>

<script setup>
import icon from '../assets/checkmark-circle-outline.svg'
</script>

<style scoped>
i {
  display: inline-block;
  width: 50px;
  height: 50px;
  color: red;
}
</style>

啪,完美~

image

本篇完。

……

……

……

意料之外的 SVG 文件问题

等一下,还没完~

开源的东西总是兼顾了很多场景,也经过了千万次的验证,因此特别靠谱也特别理想。但是在实际的开发中往往没有这么理想的情况。比方说我们的设计师给我导出了一个这样的 Check.svg 图标:

image

和开源的 ionicons 比起来,第一眼印象就是它是看不见的(透明)。放入 VSCode 对比两者的代码,也可以发现区别:

image

最后放在浏览器看,<i></i> 标签的属性它并没有继承和生效:

image

通过对其 XML 文件的分析不难看出,我方设计师给导出的图标,写死了大小和颜色,导致无法继承。

解决方法有二:

  1. 叫设计师按照我们的意愿重新导出;
  2. 修改 XML 文件,去掉 <svg> 标签内的 widthheight,同时把所有的 fill 属性都改为 currentColor

方法 1 是最理想的,但是受限于人力安排和所用的软件等问题,不一定能满足我们的需要,因此我们常常被迫使用第二种方式。