项目中的iconfont使用指南

3,868 阅读3分钟

前言


最近在做练手项目时,学到了很多关于icon使用相关的知识,特写此篇总结。

为什么要用iconfont


最开始的时候,一个项目的所有图标都是用图片来表示的。之后发现这样太过占用资源于是把所有的图标都放在一起构成精灵图(雪碧图),然后利用 css 的 background-position 定位显示不同的 icon 图标。当然这样也有很多缺点,例如每次修改都需要把精灵图整个修改,特别麻烦。

这时候出现了字体图标,这个东西可以用文字来实现图标,不仅容易操作也不占用资源。

优雅的使用iconfont


这部分参考了带你优雅的使用icon这篇文章。

iconfont共有三种用法,Unicode、font-class、svg。前两种比较容易,而且如果不是为了兼容性一般用svg就好,所以就不记录了。这里主要是讲如何在项目中高效的使用svg方式

创建icon组件

首先,我们先创建icon组件并进行全局注册

//创建组件
<template>
  <svg class="svg-icon"
       aria-hidden="true">
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script>
export default {
  name: 'icon-svg',
  props: {
    iconClass: {
      type: String,
      required: true
    }
  },
  computed: {
    iconName () {
      return `#icon-${this.iconClass}`
    }
  }
}
</script>

<style>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>
import SvgIcon from '@/components/SvgIcon'

//全局注册
createApp(App).component('svg-icon',SvgIcon)
//局部使用
<svg-icon icon-class='rili'></svg-icon>

使用svg-sprite

从iconfont上选好图标之后下载是下面这样的

├── demo.css       
├── demo_index.html
├── iconfont.css
├── iconfont.js
├── iconfont.json
└── iconfont.ttf                  

其中demo_index.html是三种方法的使用教程,如果要使用svg格式必须在项目中引入iconfont.js文件。但是采用这种方式会出现和之前使用雪碧图一样的麻烦,如果我想要添加或者删除svg,都需要重新下载这样一个js文件,所用我们使用svg-sprite-loader来制作自己的svg-sprite

vue.config.js配置如下

    //这里是因为vue-cli默认情况下会使用 url-loader 对svg进行处理,会将它放在/img`目录下
    //所以我们使用exclude把src/icons文件夹排除在外
    config.module
      .rule('svg')
      .exclude.add(resolve('src/icons'))
      .end()
    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()

现在,我们使用svg就不需要在项目中引入iconfont.js了。

自动导入svg

上一步结束之后,我们就可以如下使用icon了

//引入icon
import '@/src/icons/**.svg; 
//使用icon
<svg-icon icon-class='**'></svg-icon>

但是到现在还是不够好!如果我们项目中使用了成百上千个图标,总不能写几千行的import吧。所以这里需要使用require.context来自动导入svg文件。

require.context

require.context是webpack的一个函数,它有三个参数

  • directory:需要检索的目录
  • useSubdirectories:是否检索子目录
  • regExp: 用于匹配的正则表达式

它的返回值同样是一个函数,这个函数同时有三个属性(在js中函数也是个对象)

  • resolve {Function} -接受一个参数request,request为test文件夹下面匹配文件的相对路径,返回这个匹配文件相对于整个工程的相对路径
  • keys {Function} -返回匹配成功模块的名字组成的数组
  • id {String} -执行环境的id,返回的是一个字符串,主要用在module.hot.accept,应该是热加载? 这个函数的接受keys中一个名字,同时导入对应的模块。

简单来说我们使用req = const require.context('./***', false, /.*/)来把函数赋值给files, 这时调用req(req.keys()[0])就会自动导入第一个匹配的文件。


所以我们就可以使用下面的语句来自动导入svg

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

require.context另一个使用:自动导入路由或者store模块

const modulesFiles = require.context('./modules', true, /\.js$/)

const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

上面的代码和之前的区别在于,导入store模块不是直接import,而是以对象形式放到vuex的构造函数里。 所以这里使用一个对象来存储模块。其中value.default是为了获取模块的内容(因为调用函数只是引入模块,但我们这里需要知道模块具体的内容),modulePaht.replace()是把路径./app.js变成对象的键值app

最后


至此,我们就可以在项目中以一直比较优雅的方式使用icon了。在后续只需要把新添加的svg添加到对应的./icons/svg文件夹里就行了。

我们可以在项目的任何地方使用<svg-icon icon-class='***'></svg-icon>来使用icon。