vue 后台管理必备——iconFont的(优雅)使用

2,344 阅读4分钟

演进史


众所周知,当在项目中大量使用图片,随之而来肯定是大量的请求,为了优化所以出现了img sprite,就是所谓的精灵图(雪碧图),就是将许多的小图标都放到一张图片上,利用css的background-position来显示不同位置的图标。这也有一个很大的痛点,每每新增或删除一个图标,就需要改动一次图片,造成之前的缓存失效。

font库

学生时代使用的有font Awesome,用起来也蛮方便,但是图标量很少,常常找不到合适的图标。

(2021.7.25)font Awesome整体网站的风格已经改版了。图标量也增多了不少。

iconfont

阿里做的一个开源图标库,三大特点:原创设计,海量图标,交流协助。可以直接将源代码下载到本地,进行引入,流程较为简单高效。

iconfont食用方式


unicode

unicode是字体在网页端最原始的引用方式,特点是

  • 支持按字体的方式去动态调整图标大小, 颜色等等
  • 默认情况不支持多色,直接添加多色图标会去色

使用方法:第一步:自定义fontface

@font-face {
  font-family: 'moonfont';
  src: 
       url('iconfont.woff2?t=1627183685551') format('woff2'),
       url('iconfont.woff?t=1627183685551') format('woff'),
       url('iconfont.ttf?t=1627183685551') format('truetype');
}

第二步:定义使用iconfont的样式

.moonfont {
  font-family: "moonfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

第三步:挑选相应图标并获取编码

<span class="moonfont">&#x33;</span>

fontClass

fontClass主要解决unicode书写不直观,语义不明确的问题。 特点如下:

  • 书写直观
  • 使用class来定义图标, 所以当替换图标时,只需要更改unicode的引用即可 使用方法:第一步:引入源代码中的css文件
<link rel="stylesheet" href="./iconfont.css">

第二步:挑选类名

<span class="moonfont moonxxx"></span>

symbol

随着某浏览器淡出舞台,svg-icon使用形式慢慢成为主流。

  • 支持多色图标了,不再受单色限制。
  • 支持像字体那样通过font-size,color来调整样式。
  • 支持 ie9+
  • 可利用CSS实现动画。
  • 减少HTTP请求。
  • 矢量,缩放不失真
  • 可以很精细的控制SVG图标的每一部分 使用方法:第一步:引入源代码中的iconfont.js文件
<script src="./iconfont.js"></script>

第二步:加入通用的css代码(只需编写一次)

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

第三步:挑选相应类名

<svg class="icon" aria-hidden="true">
  <use xlink:href="#xxx"></use>
</svg>

还有一个就是 svg 是一个真正的矢量,不管你再怎么的放缩它都不会失真模糊,而且svg可以控制的属性也更加的丰富,也能做出更加生动和复杂的图标。现在ui设计师平时都喜欢使用 sketch 来工作,只要轻松一键就能导出 svg 了,所以 svg 也更受设计师的青睐。Inline SVG vs Icon Fonts 这篇文章详细的比较了 svgicon-font的优劣,大家可以去看看。PS:这里其实还用到了 SVG Sprite 技术。简单的理解就是类 svg 的似雪碧图,它在一个 svg 之中运用 symbol 标示了一个一个的 svg 图标,这样一个页面中我们遇到同样的 svg 就不用重复再画一个了,直接使用<use xlink:href="#icon-QQ" x="50" y="50" /> 就能使用了,具体的细节可以看这篇文章开头的文章 未来必热:SVG Sprite技术介绍,在之后的文章中也会手摸手叫你自己如何制作 SVG Sprite

创建icon-component组件


有了图标,接下来就是如何在项目中优雅食用它了。

<template>
  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'

export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  computed: {
    isExternal() {
      return isExternal(this.iconClass)
    },
    iconName() {
      return `#icon-${this.iconClass}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    },
    styleExternalIcon() {
      return {
        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
      }
    }
  }
}
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover!important;
  display: inline-block;
}
</style>

//引入svg组件 
import IconSvg from '@/components/IconSvg' 

//全局注册icon-svg 
Vue.component('icon-svg', IconSvg) 

//在代码中使用 
<icon-svg icon-class="password" />

进一步改造


目前来说还有一个致命的缺点,就是现在所有的svg-sprite都是通过iconfont的iconfont.js生成的。

  • 首先它是用js来生成的一段代码,所有图标icon都很不直观

1 (1).jfif 这就导致每次增删改查都要整个替换掉js文件。

  • 其次他也做不到按需加载,不能根据我们使用了哪些svg动态生成svg-sprite
  • 自定义性差
  • 添加不友善

使用svg-sprite-loader

该loader可以将多个svg打包成svg-sprite

接下来介绍如何在vue-cli的基础上进行改造,加入svg-sprite-loader

我们发现默认情况下,vue-cli会使用url-loader进行处理,会将它放在/img目录下,所以不能直接引入svg-sprite-loader,这会引发一些冲突。

最安全有效的方式是使用exclude,include关键字,指定你的svg需要被svg-sprite-loader处理。

微信截图_20210725155548.png

单这样还是不够优雅, 如果我有100个svg,不可能每个都手动引入,接下来就该require.context出场了!

自动导入


首先我们创建一个专门放置图标的icon文件夹如:@/src/icon,将所有的icon放在这个文件夹下。之后就要用到webpack的 require.context,对于这个api,直白的解释如下

require.context("./test", false, /.test.js$/); 这行代码就会去 test 文件夹(不包含子目录)下面的找所有文件名以 .test.js 结尾的文件能被 require 的文件。 更直白的说就是 我们可以通过正则匹配引入相应的文件模块。

require.context有三个参数

  • directory:说明需要检索的目录
  • useSubdirectories:是否检索子目录
  • regExp:匹配文件的正则表达式

了解这些之后,就可以这样引入@/src/icons下面所有的图标了

const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg',false,/\.svg$/)
requireAll(req)

之后我们增删改图标直接修改文件夹下的对于图标就好了,什么都不用管

更进一步优化自己的svg


这时候我们就要使用另一个很好用的东西了-- svgo

SVG files, especially exported from various editors, usually contain a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result.

它支持几十种优化项,非常的强大,8k+的star 也足以说明了问题。

详细的操作可以参照 官方文档 张鑫旭大大的文章(没错又是这位大大的文章,或许这就是大佬吧!)本文就不展开了。

写在最后



参考来源:juejin.cn/post/684490…