Vue3+vite+ts 中使用svg-icon

5,914 阅读1分钟

前言

svg-icon 在我们日常项目中经常会用到,今天我们在Vue3+vite+ts 的环境下引入一下svg-icon

创建文件

目录结构如下:

image.png

代码

index.ts文件

代码如下:

import { readFileSync, readdirSync } from 'fs'

let idPerfix = ''
const svgTitle = /<svg([^>+].*?)>/
const clearHeightWidth = /(width|height)="([^>+].*?)"/g
const hasViewBox = /(viewBox="[^>+].*?")/g
const clearReturn = /(\r)|(\n)/g

// 查找svg文件
function svgFind(e: any) {
  const arr = []
  const dirents = readdirSync(e, { withFileTypes: true })
  for (const dirent of dirents) {
    if (dirent.isDirectory()) arr.push(...svgFind(e + dirent.name + '/'))
    else {
      const svg = readFileSync(e + dirent.name)
        .toString()
        .replace(clearReturn, '')
        .replace(svgTitle, ($1: any, $2: any) => {
          let width = 0,
            height = 0,
            content = $2.replace(clearHeightWidth, (s1: any, s2: any, s3: any) => {
              if (s2 === 'width') width = s3
              else if (s2 === 'height') height = s3
              return ''
            })
          if (!hasViewBox.test($2)) content += `viewBox="0 0 ${width} ${height}"`
          return `<symbol id="${idPerfix}-${dirent.name.replace('.svg', '')}" ${content}>`
        }).replace('</svg>', '</symbol>')
      arr.push(svg)
    }
  }
  return arr
}

// 生成svg
export const createSvg = (path: any, perfix = 'icon') => {
  if (path === '') return
  idPerfix = perfix
  const res = svgFind(path)
  return {
    name: 'svg-transform',
    transformIndexHtml(dom: String) {
      return dom.replace(
        '<body>',
        `<body><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">${res.join('')}</svg>`
      )
    }
  }
}

在vite.config.js中引入插件

import { createSvg } from './src/icons/index'
export default ({ mode }) =>  defineConfig({
  plugins: [
    ...
     // svg 引入
    createSvg('./src/icons/svg/')
    ]
})

声明svg类型文件

shims-vue.d.ts

declare module "*.svg" {
  const content: any;
  export default content;
}

SvgIcon组件

代码如下:

<template lang="">
  <svg :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>
<script>
  import { defineComponent, computed } from 'vue'
  export default defineComponent({
    name: 'SvgIcon',
    props: {
      iconClass: {
        type: String,
        required: true
      },
      className: {
        type: String,
        default: ''
      }
    },
    setup(props) {
      const svgClass = computed(() => {
        if (props.className) {
          return 'svg-icon ' + props.className
        } else {
          return 'svg-icon'
        }
      });
      const iconName = computed(() => {
        return `#icon-${props.iconClass}`
      })
      return {
        svgClass,
        iconName
      }
    }
  })

</script>
<style lang="css">
  .svg-icon {
    width: 1em;
    height: 1em;
    fill: currentColor;
    vertical-align: middle;
  }
</style>

注入全局组件SvgIcon

import { createApp } from 'vue'
import App from './App.vue'
import SvgIcon from '@/components/SvgIcon/index.vue'// svg组件

const app = createApp(App)
app.component('svg-icon', SvgIcon)
app.mount('#app') // 将页面挂载到 root 节点

SvgIcon使用

<svg-icon class-name="size-icon" icon-class="size" />

演示结果

左上角的图标

image.png