11x2 精读Vue官方文档 - CookBook - 可编辑的 SVG 图标系统

509 阅读2分钟

精读 Vue 官方文档系列 🎉


基本的示例

一个 .svg 文件的内容如下所示:

<svg t="1626687392304" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1062"
    width="200" height="200">
    <path
        d="M512 1024a512 512 0 1 1 512-512 512 512 0 0 1-512 512z m-0.674909-932.398545A419.653818 419.653818 0 1 0 930.909091 511.255273 419.653818 419.653818 0 0 0 511.255273 91.601455z m139.892364 372.852363h93.765818v94.743273h-93.765818v-94.743273z m-186.181819 0h93.765819v94.743273h-93.765819v-94.743273z m-186.181818 0h93.765818v94.743273h-93.765818v-94.743273z"
        fill="#565656" p-id="1063"></path>
</svg>

一般使用 .svg 的方式有两种::

  1. 像引入图片一般,用 img 标签的 src 属性来引入一个 svg 文件。
  2. 直接将 <svg>...</svg> 标签引入到 HTML 文件中。

由于第一种方式使用条件较为苛刻,且不利于工程化处理,因此第二种方式更常用,将每个.svg 文件都视为一个独立的模块来处理。

观察 svg 代码,可以发现有两个重要标签 <svg/><path/>, 其中 <path/> 描述了具体的矢量路径,它代表了实际的图形。而 <svg/> 标签相对来说比较固定,可以控制图形的宽、高、viewbox 等。现在基于观察后的结果,我们就可以创建“可编辑的 SVG 图标系统”了,首先对 <svg/> 标签进行封装,通过外部 props 来控制其属性,然后以插槽(slot)的方式来动态引入 <path/> 标签。

“可编辑的 SVG 图标系统”的优点:

  • 图标易于实时修改。
  • 图标可以带动画。
  • 图标是内联的,所以不需要额外的 HTTP 请求。
  • 可以动态地使得图标可访问

具体实践,先定义充当标签的容器 IconBase.vue 组件。

<template>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 18 18"
    :width="width"
    :height="height"
    :aria-labelledby="iconName"
    role="presentation"
  >
    <title lang="en" :id="iconName">{{ iconName }} icon</title>
    <g :fill="iconColor">
      <slot />
    </g>
  </svg>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
  name: "IconBase",
  props: {
    width: {
      type: [Number, String],
      default: 18,
    },
    height: {
      type: [Number, String],
      default: 18,
    },
    iconName: {
      type: String,
      default: "",
    },
    iconColor: {
      type: String,
      default: "currentColor",
    },
  },
});
</script>

然后来定义具体的 svg 图标组件:IconSymbol.vue

<template>
   <path
    d="M512 1024a512 512 0 1 1 512-512 512 512 0 0 1-512 512z m-0.674909-932.398545A419.653818 419.653818 0 1 0 930.909091 511.255273 419.653818 419.653818 0 0 0 511.255273 91.601455z m139.892364 372.852363h93.765818v94.743273h-93.765818v-94.743273z m-186.181819 0h93.765819v94.743273h-93.765819v-94.743273z m-186.181818 0h93.765818v94.743273h-93.765818v-94.743273z"
  ></path>
</template>

最后来组合两个组件进行使用:

<icon-base :width="18" :height="18" :icon-name="symbol">
    <icon-symbol/>
</icon-base>

当然,你也可以直接在 iconBase.vue 中基于传入的 icon-name 来动态匹配对应的图标组件,以减少一层封装。

带动画的图标

文章给的思路很简单,使用动画库例如 gsap 然后用 $refs 来直接操作 DOM 的方式为 <path/> 标签赋予动画效果。

其它替代方案

直接在页面中引入 <svg> 内容的另一种使用方式,就是 Svg Sprite 了,简称 SVG 精灵。

你可以配置 Webpack 的 svg-sprite-loader 来一次性的将所有 svg 内容注入到 HTML 文件中,并为每个 symbol 设置 ID,最后只需要使用 <use xlink="iconId"> 标签来引入对应 symbol 的 Id 即可。

这种方式可以避免重复多次的向 HTML 插入 Svg 代码,例如在表格与列表渲染 svg 内容时。

但是带来的不足也很明显,那就是一旦 svg 内容过多,就会导致页面加载缓慢,或者是导致编译工具编译速度变慢。

最后,文章还分享了另一个 SVG 相关的工具:svgo-loader 可以对 SVG 的代码进行优化与精简,例如删除无用的注释与属性。