likes
comments
collection
share

SVG图标使用原理以及在Vite中如何解析

作者站长头像
站长
· 阅读数 15

使用原理

我们知道,svg中元素只有被引用了才会显示在页面中,否则不会显示。在我们导入svg文件后,需要将其解析成元素,并通过svg标签包裹。如下:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
  <symbol id="icon-dashboard" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 100">
    <path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"></path>
  </symbol>
  <!--?xml version="1.0" standalone="no"?-->
  <symbol id="icon-save" t="1608734152787" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1160" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs><style type="text/css"></style></defs>
    <path d="M959.390493 527.761985c0 23.127743-18.419512 41.87369-41.138956 41.87369-12.335956 0-23.276123-5.640464-30.817889-14.393825L541.089525 202.729238c-16.065907-16.353456-42.113143-16.353456-58.179051 0L237.802034 452.204126c2.877537 5.636371 4.653997 11.926634 4.653997 18.690688l0 165.475775c0 0.328481-0.089028 0.635473-0.097214 0.961908l0.297782 0 0 163.003468c0 23.125697 18.506493 41.871643 41.337477 41.871643l82.568531 0L366.562607 677.738765c0-22.850427 18.520819-41.3692 41.3692-41.3692L614.775758 636.369565c22.848381 0 41.3692 18.518772 41.3692 41.3692 0 22.846334-18.520819 41.3692-41.3692 41.3692L449.299983 719.107965l0 165.473729c0 22.427802-17.875113 40.583301-40.142256 41.246403l0 0.121773L263.148305 925.94987c-57.115835 0-103.418394-46.864354-103.418394-104.678084L159.729911 637.331473l0.084934 0c-0.00614-0.326435-0.097214-0.633427-0.097214-0.961908L159.717631 531.680225l-23.162536 23.575952c-7.541767 8.745175-18.475793 14.379499-30.807656 14.379499-22.719444 0-41.138956-18.745946-41.138956-41.87369 0-11.862165 4.895497-22.520923 12.687974-30.141484l-0.237407-0.2415 362.215602-368.668571c40.164768-40.881083 105.285928-40.881083 145.450696 0l361.569896 368.010585-0.299829 0.305969C954.175725 504.680291 959.390493 515.563152 959.390493 527.761985zM614.733802 842.206584c0.443092 0 0.854461 0.119727 1.294482 0.132006l0-0.132006 122.683157 0c22.828938 0 41.337477-18.745946 41.337477-41.871643L780.048919 637.331473l0.534166 0c-0.016373-0.496304-0.145309-0.961908-0.145309-1.464351 0-23.125697 18.417465-41.87369 41.138956-41.87369 22.719444 0 41.136909 18.747993 41.136909 41.87369 0 0.502443-0.125867 0.968048-0.145309 1.464351l0.405229 0 0 183.940312c0 57.812707-46.299489 104.678084-103.418394 104.678084l-143.528927 0 0-0.132006c-0.441045 0.014326-0.852414 0.132006-1.294482 0.132006-22.719444 0-41.138956-18.7439-41.138956-41.869596C573.594847 860.9556 592.014358 842.206584 614.733802 842.206584z" p-id="1161"></path>
  </symbol>
</svg>

然后我们通过引用对应的来获得对应的图标

<svg>
  <use :href="#icon-save"/>
</svg>

在svg中,是一个特殊的元素,当使用这个元素时,需要通过一个标识关联对应的,这个标识就是href。和通过href属性值与id建立引用关系。如:href="#icon-save"对应 id="icon-save"。

将元素作为组件封装

有了上述的基础知识,我们就可以封装一个SvgIcon组件,组件可以提供name、color、className三个属性,分别对应图标名称、颜色、以及自定的css类名。

<template>
  <svg
    :class="svgClass"
    aria-hidden="true"
    v-on="$attrs"
    :style="{ color: color }"
  >
  	<use :xlink:href="iconName" />
  </svg>
</template>
<script lang="ts">
import { computed } from "vue";
export default {
  name: "SvgIcon",
  props: {
    name: {
      type: String,
      required: true,
    },
    color: {
      type: String,
      default: "inherit",
    },
    className: {
      type: String,
      default: "",
    },
  },
  setup(props) {
    const iconName = computed((): string => `#icon-${props.name}`);
    const svgClass = computed((): string => {
      if (props.className) {
        return `svg-icon icon-${props.name}`;
      } else {
        return "svg-icon";
      }
    });
    return { iconName, svgClass };
  },
};
</script>
<style scoped>
  .svg-icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
  }
</style>

有了这个组件,我们就可以方便的引用对应的元素,而不必写大量的重复代码。

Tip: fill:currentColor是什么意思?

fill

  1. fill 是SVG元素的一种属性;SVG元素的这些属性,用于指定如何处理或者呈现元素的详细信息
  2. 比如,fill ,对于形状元素和文本,它定义了绘制元素的颜色。对于动画,它定义了动画的最终状态。在下面的例子中,它定义了图标的颜色。

currentColor

  1. 在css中,currentColor是一个变量,这个变量的值是当前元素的color值。
  2. 如果当前元素没有在CSS里显示地指定一个color值,那它的颜色值就遵从CSS规则,从父元素继承而来。

iconfont为何要设置vertical-align: -0.15em?

因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果。

解析成元素

的内容来自于svg文件,文件内容如下:

<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg">
  <path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/>
</svg>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           </svg>

文件中包含了一些无用的信息,width、height以及可能存在的"\r\n",我们通过正则表达式匹配这些文本然后做对应的处理。

  1. 匹配svg开闭合标签:/<svg([^>+].*?)>/
  2. 匹配宽高属性:/(width|height)="([^>+].*?)"/g
  3. 匹配viewBox属性:/(viewBox="[^>+].*?")/g
  4. 匹配回车换行属性:/(\r)|(\n)/g

有了待处理的内容,我们就可以经处理过程封装成一个vite插件。

export const svgBuilder = (path, perfix = 'icon') => {
  if (path === '') return
  const res = dealSvgFile(path)//获取处理好的svg内容
  return {
    name: 'svg-transform', // 必须的,将会在 warning 和 error 中显示
    transformIndexHtml(html) { //转换 index.html 的专用钩子
      return html.replace(
        '<body>',
        `
          <body>
            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
              ${res.join('')}
            </svg>
        `
      )
    }
  }
}

dealSvgFile通过node读取svg文件的内容,然后使用对应的正则处理对应的字符串。然后插件通过transformIndexHtml钩子,将处理完的内容插入的body中,注意:svg标签设置了position:absolute,并设置宽高为0,这是为了让svg标签不显示在页面中。

vite中配置插件

在项目根目录下的vite配置文件 vite.config.ts 中进行配置:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { svgBuilder } from './src/plugins/svgBuilder';

export default defineConfig({
    plugins: [
      vue(),
      svgBuilder('./src/assets/svg/') // 这里已经将src/icons/svg/下的svg全部导入,无需再单独导入
    ]
})

图标使用

<svg-icon name="save"/>
<svg-icon name="dashboard" color="red"/>

插件具体实现参考:juejin.cn/post/705145…