使用 SVG 图标: (1) 编写 Icon 组件

7,614 阅读5分钟

SVG 即可缩放矢量图形。跟普通的图片相比,它放大也不会失真,并且文件体积更小。现在浏览器对 SVG 支持性是非常好的,我们经常在项目中使用它,比如使用 SVG 图标或者使用 SVG 图片解决高清屏幕下的 1px 显示问题

本文将主要谈论 SVG 图标的使用,并且会结合实例给出相应的解决方案。文章将分为上下两篇: 上篇,主要介绍 SVG 的相关知识,介绍如何使用 gulp 合并图标,并编写一个 Icon 组件。下篇中,将介绍如何编写 webpack 插件来处理 SVG 文件。

1. SVG 图标

使用 SVG 做图标的话,就不得不拿来与 iconfont 做比较。iconfont 的兼容性比较好,但在显示效果上并不如 SVG,并且不支持多色图标。所以现在是推荐使用 SVG 图标

在项目中使用 SVG 图标有多种方式,其中一种是 SVG Sprite,即把各个图标合并成一张背景图片。但这种方式非常不灵活,比如我们无法调整某个图标的大小或者颜色。

相比之下,一种更好的方式使用 SVG Symbol。把个个图标合并成一个包含多个 symbol 的 SVG 文件。在需要使用图标的地方,引用对应的 symbol 即可。

假设有下面这一段 symbol 文件,里面包含了一个 id 为 QQ 的图标。

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <symbol id="QQ" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
    <path d="M2.87 23c-1.42 3.49-1.67 6.78-.54 7.37.79.44 2.06-.54 3.24-2.31a9.73 9.73 0 0 0 3.24 5.11C7.09 33.82 6 34.9 6 36.08c0 2 3 3.58 6.78 3.58 3.39 0 6.19-1.28 6.73-3h.79c.54 1.72 3.34 3 6.73 3 3.78 0 6.78-1.57 6.78-3.58 0-1.18-1.13-2.26-2.85-2.9a9.73 9.73 0 0 0 3.24-5.11c1.18 1.77 2.41 2.75 3.24 2.31 1.18-.59.93-3.93-.54-7.37-1.13-2.7-2.65-4.71-3.83-5.16v-.54a5.32 5.32 0 0 0-.79-2.8v-.2A2.92 2.92 0 0 0 32 13C31.69 5.93 27.18.33 19.86.33S8 5.93 7.73 13a2.92 2.92 0 0 0-.29 1.33v.2a5.32 5.32 0 0 0-.79 2.8v.54C5.57 18.31 4 20.27 2.87 23z"/>
  </symbol>
 </svg>

现在要使用这个图标,通过设置 id 就可以引入对应的 symbol。

<svg class="icon icon-QQ">
  <use xlink:href="#QQ"></use>
</svg>

我们给图标添加了一个类名,如有必要,还可以通过 CSS 就可以来调整图标的样式。这种方式可控性强,并且显示效果也好。

2. gulp-svg-sprite 的使用

设计师给我们的图标都是一张张图片,差不多像下图这样。

icon 文件夹

接下来需要对 SVG 进行合并压缩处理。最省事的方式是使用 阿里巴巴矢量图标库 去管理图标,需要使用 symbol 文件的话,在页面上引入脚本即可。如果你喜欢这种方式,那下文就可以不必再看了😂。

在这里,我们使用 gulp 以及 gulp-svg-sprite 来处理 SVG 图标。gulp-svg-sprite 是一个专门用来处理 SVG 图片的 gulp 插件,提供了生成 sprite 背景图片与 symbol 文件的功能。下面是 gulp 的配置文件

const gulp = require('gulp')
const svgSprite = require('gulp-svg-sprite')
const config = {
  // 去除 svg icon 颜色
  svg: {
    transform: [
      function (svg) {
        return svg.replace(/(<style.*?<\/style>)/g, '').replace(/(fill=\"#([0-9a-f]{6})\")/g, '')
      }
    ]
  },
  mode: {
    view: {
      bust: false,
      sprite: 'sprite.svg',
      render: {
        css: true
      }
    },
    symbol: true
  }
}

gulp.task('svgSprite', function () {
  return gulp.src('./icon/*.svg').pipe(svgSprite(config)).pipe(gulp.dest('svg'))
})

gulp.task('default', ['svgSprite'])

通过设置 mode 来配置生成不同类型的文件。上述配置中,我们选择生成 spirite 背景图片与 symbol 文件。有些 SVG icon 中预设的有些颜色,在合并成 symbol 文件前,要做去色处理。配置插件的 transform 函数,即可完成对去色。

在项目文件夹目录下执行 gulp 命令,生成的文件如下。

svg文件夹

接下来拷贝 sprite.symbol.svg 的内容拷贝到 index.html 中,并且设置 SVG 元素隐藏。之后 Icon 组件就可以使用了。这一步操作是手动进行的,非常不方便,所以在下篇中,我们通过开发 webpack 插件来自动插入 symbol 文件内容到 index.html 中。

<!-- index.html -->
<body>
    <!-- 拷贝 symbol 文件到这里 -->
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none;">
        <symbol id="QQ">
        <!-- .... -->
        </symbol>
    </svg>
    <div id="app"></div>
</body>

3. Vue Icon 组件的实现

现在开发的 Icon 组件提供哪些功能或者有哪些参数呢?暂时提出以下几点需求:

  1. 设置 type 值指定要用的图标
  2. 可以定制图标的尺寸
  3. 可以设置图标的颜色

一个示例如下:

<template>
  <svg :class="iconClassName" :style="{color: this.color}">
    <use :xlink:href="'#' + type"></use>
  </svg>
</template>
<script>
export default {
  name: 'icon',
  computed: {
    iconClassName () {
      return ['icon', `icon-${this.size}`, `icon-${this.type}`]
    }
  },
  props: {
    type: {
      required: true,
      type: String,
      validator (value) {
        return ['wechat', 'QQ', 'weibo'].indexOf(value) !== -1
      }
    },
    size: {
      type: String,
      default: 'medium',
      validator (value) {
        return ['small', 'medium', 'large'].indexOf(value) !== -1
      }
    },
    color: {
      type: String,
      default: '#cccccc'
    }
  }
}
</script>
<style lang="scss">
.icon {
  display: inline-block;
  vertical-align: middle;
  fill: currentColor;
  &.icon-medium {
    width: 24px;
    height: 24px;
  }
  // ...
}
</style>

Icon 组件有三种大小的尺寸,默认情况下是 medium。图标颜色可以根据自定义,默认是 #cccccc。在合并 SVG 图标时,去除了图标本身的颜色。设置 Icon 的样式规则中有这一条 fill: currentColor;,这段属性表明 SVG 路径填充色继承当前元素的颜色,这样我们就可以定制图标的颜色了。

针对 Icon 组件的两个props: type 与 size,编写了 validator 函数来校验传入 prop 值,当传入的 prop 值非法时,开发环境下会有错误的提示,从而避免了 Icon 组件使用不正确的问题。

组件注册后,就可以直接使用了。

<icon type="QQ" />

完整的代码点击这里

4. 总结

在本篇文章,我们完成了 SVG 图标的合并与 Icon 组件的编写。SVG 图标因为其自身的优势与浏览器的良好支持,非常适合在项目中使用。希望大家可以探索出更多的用法。

下篇文章中,我们将通过开发 webpack 插件,避免手动的复制粘贴 SVG symbols 文件。

参考资料