项目中优雅的使用svg

搭建环境获取图标

我们使用vue-cli3搭建项目,怎么样才算优雅?
首先我们在src目录下新建icons/文件夹,在icons/文件夹下建svg/文件夹,将来我们项目中的svg图标都会统一放在这里,去阿里图标库下载svg图标
项目中优雅的使用svg
点击svg下载到icons/svg目录下修改文件名为qq.svg,或者是在icons/svg目录下新建一个qq.svg文件,把复制的svg代码放进去也可以,这样就获取到了一个图标,很easy.

处理svg图标

ue-cli对svg文件有默认的url-loader 处理,我们要使用svg 图标需单独进行配置

下载一个插件svg-sprite-loader来单独处理我们的svg图标,它是一个webpack loader,支持将多个svg打包成svg sprites,安装插件

npm install svg-sprite-loader -D

我们要怎么使用它呢,首先我们不能覆盖原有的svg解析loader,我们只需要把icons/svg这个文件夹下的svg文件解析打包即可,我们在vue.config.js中chainWebpack函数中配置,来看代码:
// 内置路径包

const path = require("path");

// 定义resolve方法,获取绝对路径
function resolve(dir) {
  return path.join(__dirname, dir);
}

module.exports = {
  // 一个函数,会接收一个基于 webpack-chain 的 ChainableConfig 实例
  // 允许对内部的 webpack 配置进行更细粒度的修改
  chainWebpack: config => {
    // 配置svg默认规则排除icons目录中svg文件处理
    config.module
      .rule("svg")
      .exclude.add(resolve("src/icons"))
      .end();

    // 新增icons规则,设置svg-sprite-loader处理icons目录中svg文件
    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();
  }
}

如果我们不清楚cli的默认配置,怕改错,可通过vue inspect审查webpack内部配置,
上面代码中我们使用了webpack的链式高级用法来处理loader,首先排除了默认svg的loader对我们icons/目录下svg文件的处理,然后新增了一个规则让svg-sprite-loader处理我们icons/文件夹下的svg文件,最后我们设置了icon-加上经过处理的svg文件名作为symbolId,也就是说我们在使用qq.svg时可以直接在use标签使用#icon-qq,代码中我们引入了path这样一个内置的包,定义了一个resolve方法,该方法主要是来获取文件绝对路径的,我们把使用路径的地方都使用该方法转为绝对路径,当然使用相对路径也是可以的,但是不太安全,平台解析相对路径有差异性,所以绝对路径是最安全的.

svg sprites图标简单使用

现在我们就可以在你想使用图标的位置使用了,使用方式如下

在main.js中引入(全局引入)要使用的图标文件

import "@/icons/svg/qq.svg";
<svg>
  <use xlink:href="#icon-qq"></use>
</svg>

你以为这就完了?不,还远远不够,这样使用一个图标就得引入文件一次也太麻烦了,接着看下文

进阶:svg文件自动引入

知道为什么在icons/文件夹下还有一个存放svg文件的svg/文件夹吗,就是为了这一步自动化引入准备的,我们在icons/文件夹下新建index.js文件,两行代码搞定,内容如下:

// icons图标自动加载
const req = require.context("./svg", false, /\.svg$/);
req.keys().map(req);

上面代码中我们使用require.context设置了当前目录下的./svg文件为上下文,使用正则匹配了它需要检测的文件名,这样它就会在当前目录的svg文件夹下去匹配符合规则的文件名
然后我们使用req.keys拿到所有文件名数组,再使用map遍历加载req方法,这样当该文件被调用时会遍历加载所有匹配到的文件,这就很nice了.

看看我们改进后的使用方法:
注释掉之前的代码,在main.js中引入icons/index.js文件

import "@/icons/index.js";

模板中使用和上面一样,不过这次改进当我们再次下载了一个svg图标时,不用再引入一遍图标svg了,因为我们做了自动化,icons/svg/下的svg后缀图标文件都可被自动引入

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

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

是不是很方便,你以为结束了?不,我们还可以再简化,因为每次使用都得svg标签包着use太麻烦了,写着也不太雅观,我们继续简化,一定要看起来使用起来都十分优雅.

完美版:SvgIcon组件

在components/目录下新建SvgIcon/index.vue文件,我们写一个svgicon组件,封装一下再全局注册,这样使用起来就会很方便了!

svg-icon组件代码如下:

<template>
  <svg :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>
<script>
export default {
  name: "SvgIcon",
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ""
    }
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`;
    },
    svgClass() {
      if (this.className) {
        return "svg-icon " + this.className;
      } else {
        return "svg-icon";
      }
    }
  }
};
</script>
<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style

当然组件内部我们还可以根据自身项目情况进行扩展,我这边写了基础的配置

组件写好了之后我们在icons/index.js中进行全局注册,这样我们只引入这一个文件就可以达到自动加载和组件注册两个功能

icons/index.js改进如下:

import Vue from "vue";
import SvgIcon from "@/components/SvgIcon";

// icons图标自动加载
const req = require.context("./svg", false, /\.svg$/);
req.keys().map(req);

// 全局注册svg-icon组件
Vue.component("svg-icon", SvgIcon);

最后就是我们的使用了,在main.js文件引入icons/index.js

import "@/icons/index.js";

再来看看我们使用图标的方法,组件中:

<template>
  <svg-icon icon-class="qq" class-name="qq-style"></svg-icon>
</template>

看,我们只用在icon-class中传入要使用的图标文件名就可以了,当然class-name还可以传入一个类,进行一些简单的样式修改,是不是很优雅,你get到了吗?

上一篇:pub哥的2020文章清单


下一篇:582 svg入门案例