Skip to content

vue使用svg

svg是非常好用的图标格式,具有缩放不失真,可以自定义颜色等多项优点

但是在vue中使用svg往往需要再配置,这里用vue3 + ts举例

需要安装依赖

yarn add svg-sprite-loader -D

yarn add fs

确立svg的存放文件夹,在vite.config.ts中配置,大致:

js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { createSvg } from './src/assets/icons/index'
 
export default defineConfig({
  plugins: [
    vue(),
    // svg 引入
    createSvg('./src/assets/icons/svg/')
  ],
})

其中createSvg则这个方法来自自定义的ts文件

typescript
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) {
  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, $2) => {
                            let width = 0,
                                height = 0,
                                content = $2.replace(clearHeightWidth, (s1, s2, s3) => {
                                    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>`
        )
    }
  }
}

并且需要建立svg组件

vue
<template>
    <svg :class="svgClass" v-bind="$attrs" :style="{ color: color }">
        <use :xlink:href="iconName"></use>
    </svg>
</template>

<script setup lang="ts">
import { computed, defineProps } from 'vue'
const props = defineProps({
    name: {
        type: String,
        required: true
    },
    color: {
        type: String,
        default: ''
    }
})
const iconName = computed(() => `#icon-${ props.name }`)
const svgClass = computed(() => {
    if(props.name) return `svg-icon icon-${ props.name }`
    return 'svg-icon'
})
</script>

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

在app中注册这个组件,如

typescript
import { createApp } from 'vue'
import App from './App.vue'
import svgIcon from './icons/index.vue'

const app = createApp(App)

app
.component('svg-icon', svgIcon)
.mount('#app')

最后就可以使用了,svgIcon组件中name名字,color为自定义颜色

值得注意的是自定义颜色可能不发生改变,这很可能是svg文件中存在fill这个属性导致的,删除全部fill即可。

鄂ICP备19018246号-1