Iconfont使用技巧

Posted by Youzi Blog on October 10, 2019

封装 Icon 的 Vue 组件实例

前言

本文主要介绍使用阿里矢量图标库iconfont.cn,在 Vue 项目中对 icon 进行组件封装,使得图标库便于后续开发使用。

关于 icon-font 的 4 种使用方法

官网介绍在代码中的应用有 4 种方式https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.16&helptype=code

  1. 直接下载单个图标文件,在代码中引入,可以是 png、svg 等格式,这种方式适用于图标临时使用、或者只是单独使用的场景,好处是可以自选颜色,方便快捷;
  2. Unicode 引用,类似字体文件的引入,好处是兼容性强,支持 ie6 及以上版本浏览器,由于是字体,所以可以用font-size / color去改变大小和颜色,但不支持多色图标,具体使用代码可以参考上面的链接;
  3. font-class 引用,由于 Unicode 方式使用不够直观,不知道具体引用的是哪个图标,说白了就是不够语义化,本质和 Unicode 引用是一样的,兼容 ie8 及以上;
  4. symbol 引用,传闻是未来主流方式,那就用这种方式来建立 Vue 组件吧。

symbol 方式创建全局图标组件

IconFont 官方建议使用线上版本,即在线引入 svg,但这样做会造成每次增删改图标时,生成的 JS 链接都会改变,所以每次都得改引用的 url,而我一般会把生成的 JS 代码复制到本地,然后通过本地全局import,步骤如下;

  1. 建立icon(folder) -> index.js(jsFile) && iconfont.js(jsFile),复制生成的代码到iconfont.js里,index.js里引入;
  2. 建立公共组件icon-svg.vue,代码如下,#icon-xxxx是按照 IconFont 规范组合成的超链接名字,其他的 props 可以根据需要增删。

<!-- usage -->
<!-- <icon-svg name="xxxx" /> -->
<template>
  <svg
    :class="getClassName"
    :width="width"
    :height="height"
    aria-hidden="true">
    <use :xlink:href="getName"></use>
  </svg>
</template>

<script>
  export default {
    name: 'icon-svg',
    props: {
      name: {
        type: String,
        required: true
      },
      className: {
        type: String
      },
      width: {
        type: String
      },
      height: {
        type: String
      }
    },
    computed: {
      getName () {
        return `#icon-${this.name}`
      },
      getClassName () {
        return [
          'icon-svg',
          `icon-svg__${this.name}`,
          this.className && /\S/.test(this.className) ? `${this.className}` : ''
        ]
      }
    }
  }
</script>

<style>
  .icon-svg {
    width: 1em;
    height: 1em;
    fill: currentColor;
    overflow: hidden;
  }
</style>

  1. 在 index.js 里引入 Vue 和 IconSvg 组件,全局注册后使用。

这种方式有一个很烦的地方就是如果图标频繁更换,那就需要一直改动icon-font.js文件,大佬们想出了svg-sprite雪碧图的方式,来看看怎么整的。

svg-sprite

  1. 在本地建立src -> icons(folder)保存所有用到的 svg 图标,安装svg-sprite-loader,它是一个webpack-loader,可以将多个 svg 打包成一个svg-sprite,和雪碧图思想一样的,先画好所有的 svg,用 symbol 标示每个图标,这样同一页面中遇到相同的 svg 就不用重复再绘制了;
  2. 对组件进行改造,首先由于很多项目脚手架自带有url-loader插件,没有的可以跳过,也会对后缀名为 svg 的文件进行处理,而我们要对 svg 图标进行处理,所以更改build -> webpack.base.conf.js文件,利用 webpack 的 include 和 exclude,只针对 icon 的 svg 做处理,代码如下,完成这步之后,可以用import '@/src/icons/qq.svg;这段代码引入icon,用<svg><use xlink:href="#qq" /></svg>来使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  test: /\.svg$/,
  loader: 'svg-sprite-loader',
  include: [resolve('src/icons')]
},
{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  loader: 'url-loader',
  exclude: [resolve('src/icons')],
  options: {
    limit: 10000,
    name: utils.assetsPath('img/[name].[hash:7].[ext]')
  }
}
  1. 那这样一个个去引入图标还是很麻烦,所以想到了用webpack的require.context,这个方法可以找到特定目录下的特定文件,再批量引入,后面会写一篇blog来介绍这个方法,在index.js里写代码如下,路径需要自己修改,最后获取svg文件名称那一步,也需要根据实际的命名规则去修改:
1
2
3
4
5
6
7
8
const svgFiles = require.context('src/icons', true, /\.svg$/)
const iconList = svgFiles.keys().map(item => svgFiles(item))
export default {
  // 获取图标icon-(*).svg名称列表, 例如[shouye, xitong, zhedie, ...]
  getNameList () {
    return iconList.map(item => item.default.id.split('-')[1])
  }
}
  1. 最后我们实现了一个脱离IconFont的组件,但是事实上使用方法是类似的,摆脱了需要频繁增删svg文件而产生的多文件导入的烦恼。

结语

这种方法更适合不怎么用fontawesome的人,并且不用兼容低版本浏览器的,大概思路就是创建svg-sprite,对<svg><use></use></svg>这样一个模板进行封装,批量引入svg文件,最后全局注册即可使用。