vue-优雅的使用svg

背景

日前,开发一个配置数据表单类的框架,需要考虑框架的扩展性,以及使用的便捷性,其中图标类如何便捷的引入也在考虑之中。

期望实现的效果是在新增一个svg时,仅将svg文件存放到某一个文件夹下,之后使用时通过类似于<mis-icon icon="name">这种方式即可使用。

思路

  1. 全局注册组件
  2. 使用js读取文件夹下的所有svg导入至项目中

实现

全局注册组件

src/compoments下创建MisIcon.vue

<template>
  <svg :class="svgClass" aria-hidden="true">
    <use :xlink:href="iconName"/>
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    icon: {
      type: String,
      required: true,
    },
    className: {
      type: String,
      default: '',
    },
  },
  computed: {
    iconName () {
      return `#icon-${this.icon}`
    },
    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>

集中存放svg文件

  1. src/assets/svg文件夹下存放所有svg文件

    注意:如果需要修改svg的颜色,svg文件中的填充色 fill 必须删除

  2. src/assets 下创建svg.js文件

    // 读取所有svg文件
    const requireAll = requireContext => requireContext.keys().map(requireContext)
    const req = require.context('/src/assets/svg', false, /\.svg$/)
    requireAll(req)
    

全局注册组件并导入svg

// main.js
import '@/assets/svg.js'
import MisIcon from '@/components/MisIcon.vue' // svg组件

// 注册全局插件
Vue.component('mis-icon', MisIcon)

重要的一步 - 配置loader

根目录下的vue.config.js文件中

const path = require('path')

function resolve(dir) {
  return path.join(__dirname, './', dir)
}

module.exports = {
  chainWebpack: config => {
    // svg 规则
    const svgRule = config.module.rule('svg') // 找到原有的svg-loader
    svgRule.uses.clear() // 清除已有的loader, 如果不清除会在原有loader之后再使用当前loader规则
    svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
    svgRule // 添加svg新的loader处理
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]',
      })

    // 修改images loader,添加svg处理
    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(resolve('src/assets/svg'))
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
  }
}

使用

<template>
  <div>
    <!-- icon的值为svg文件名 -->
    <mis-icon icon="test" className="test-icon"></mis-icon>
  </div>
</template>

<script>
export default {
  name: 'SvgTest'
}
</script>

<style scoped>
  .test-icon{
    color: blue;
    width: 200px;
    height: 200px;
    &:hover{
      color: lightblue;
    }
  }
</style>