阅读 210

Vue-CLI中使用svg icon图标

背景

最近几个项目常用到svg图片,往往步骤还不少,每次都要上网查一遍怎么配置,不如自己走一遍,深入理解下,记录下来。

配置步骤

在Vue-CLI中配置svg的icon,可以参照下面的步骤。

  1. 需要用到雪碧图的loader,解析svg。
npm install svg-sprite-loader --save-dev
复制代码
  1. 配置vue.config.js,加入sprite-loader的解析。
module.exports = {
  chainWebpack: config => {
    // 原svg规则覆盖了所有的svg图标,需要先将自己的svg排除,以应用新的sprite规则
    // src/assets/icons是我们将要存放svg的目录
    config.module
      .rule('svg')
      .exclude.add(path.join(__dirname, 'src/assets/icons')) // 排除自定义svg目录
      .end()
    config.module
      .rule('icons') // 新规则
      .test(/\.svg$/)
      .include.add(path.join(__dirname, 'src/assets/icons')) // 新规则应用于我们存放svg的目录
      .end()
      .use('svg-sprite-loader') // 用sprite-loader接卸
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()
  }
}
复制代码

  1. 创建全局SvgIcon组件,用于页面显示svg icon。组件位置:src/components/SvgIcon.vue
<template>
    <svg class="svg-icon" aria-hidden="true">
        <use :xlink:href="`#icon-${name}`" />
    </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    name: { // svg文件名称
      type: String,
      required: true
    }
  }
}
</script>

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

复制代码
  1. 在main.js中,全局引入SvgIcon组件,以及加载所有的svg图标。
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg组件

// 1. 全局注册SvgIcon组件
Vue.component('svg-icon', SvgIcon)
// 2. 载入所有svg icon
const requireContext = require.context('./assets/icons', false, /\.svg$/)
requireContext.keys().forEach(requireContext)
复制代码
  1. 将svg放入src/asset/icons目录下

  1. 在页面中引用,即大功告成。
<svg-icon name="book" /> <!-- name就是svg图标文件的名称 -->
复制代码

后记

看了好多篇关于svg的配置,往往拿来主义,直接用,有些不合理的地方也一传十,十传百。

1. 引入svg icon

原版是这样:

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

我看来看去,最后不就是执行这一句么requireContext => requireContext.keys().map(requireContext),为什么要拐那么多弯呢?而且最终目的只要把资源require进来就行了,用map也没意义,于是我修改成:

const requireContext = require.context('./assets/icons', false, /\.svg$/)
requireContext.keys().forEach(requireContext)
复制代码

关于requreiContext

a) require.context()函数功能为加载多个资源,requreiContextrequire.context()的返回结果,是一个函数,打印出来的代码是这样:

function webpackContext(req) {
  var id = webpackContextResolve(req);
  return __webpack_require__(id);
}
复制代码

这个函数的作用就是根据名称加载资源,返回资源对象,如{default:{...}}.

b) requreiContext的原型有3个属性,id、keys、resolve, 后两者为函数。keys返回所有资源的文件名列表,resolve返回资源的相对项目的完整路径。打印原型requreiContext.prototype得到如下结果:

打印requireContext.keys()得到如下结果:

2. SvgIcon组件

网上大多文章用的是这个版本:

<template>
  <svg :class="svgClass" aria-hidden="true">
    <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>
复制代码

a)发现className属性和svgClass完全没必要,引用svg-icon组件时,直接加class属性就行,会自动合并,何必画蛇添足。像这样就可以了:

<svg-icon name="book" class="myicon" />
复制代码

b)iconClass不好理解,直接取名name更简洁清晰;计算属性iconName也是完全没太大必要,毕竟组件定下了,name属性就是不变的。

修改后就变成简洁版本:

<template>
    <svg class="svg-icon" aria-hidden="true">
        <use :xlink:href="`#icon-${name}`" />
    </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    name: {
      type: String,
      required: true
    }
  }
}
</script>

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

当然,也许有性能或者其他什么方面的考量,目前我没看出加那些的必要。

3. 获取svg icon

我常去的网站是这个,阿里巴巴矢量图标库,资源很丰富。

文章分类
前端
文章标签