在Vite Vue3.2 项目优雅的使用Svg Icon
1. 安装 svg-sprite-loader
npm i svg-sprite-loader --save-dev
2. SvgIcon component
<!-- /src/components/SvgIcon/icon.vue -->
<script setup lang="ts">
import {computed, useCssModule, useAttrs} from "vue";
export interface SvgIconProps {
name: string
}
const props = defineProps<SvgIconProps>()
const styles = useCssModule()
const attrs = useAttrs()
const iconName = computed(() => `#icon-${props.name}`);
const svgClass = computed(() => {
const className = [styles['svg-icon']]
if (props.name) className.push(`icon-${props.name}`)
return className
})
</script>
<template>
<svg :class="svgClass" v-bind="attrs">
<use :xlink:href="iconName"></use>
</svg>
</template>
<style module>
.svg-icon {
/* width: 1em;
height: 1em;*/
fill: currentColor;
vertical-align: middle;
overflow: hidden;
}
</style>
// /src/components/SvgIcon/index.ts
import SvgIcon from './icon.vue'
import type {SvgIconProps} from './icon.vue'
export {
SvgIcon
}
export type {
SvgIconProps
}
3. 新建plugins
文件下新建svgBuilder.ts
// /plugins/svgBuilder.ts
import {readFileSync, readdirSync} from 'fs'
import {join as pathJoin} from 'path'
import {Plugin} from 'vite'
let idPrefix = ''
const svgTitle = /<svg([^>+].*?)>/
const clearHeightWidth = /(width|height)="([^>+].*?)"/g
const hasViewBox = /(viewBox="[^>+].*?")/g
const clearReturn = /(\r)|(\n)/g
const findSvgFile = (dir: string) => {
const svgRes: string[] = []
const directory = readdirSync(dir, {withFileTypes: true})
for (const dirent of directory) {
if (dirent?.isDirectory()) {
svgRes.push(...findSvgFile(pathJoin(dir, dirent.name, '/')))
} else {
const svg = readFileSync(pathJoin(dir, dirent.name))
.toString()
.replace(clearReturn, '')
.replace(svgTitle, ($1: string, $2: string) => {
let width = '0'
let height = '0'
let content = $2.replace(
clearHeightWidth,
(s1, s2:string, s3:string) => {
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="${idPrefix}-${dirent.name.replace(
'.svg',
''
)}" ${content}>`
})
.replace('</svg>', '</symbol>')
svgRes.push(svg)
}
}
return svgRes
}
const svgBuilder = (path: string, prefix = 'icon'): Plugin => {
idPrefix = prefix
const res = findSvgFile(path)
return {
name: 'svg-transform',
transformIndexHtml(html) {
return html.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>`
)
}
}
}
export default svgBuilder
4. 更新tsconfig.node.json
// tsconfig.node.json
{
// ...
"include": ["vite.config.ts", "./plugins/*"]
}
5. src目录下新建icons
文件夹内存放svg图标
6. vite.config.ts 中
import svgBuilder from './plugins/svgBuilder'
import {resolve} from "path";
// ...
plugins: [
// ...
svgBuilder(resolve('./src/icons'))
]
7. 使用
<script setup lang="ts">
import {SvgIcon} from "@/components/SvgIcon";
</script>
<template>
<!-- name 为 svg icon 的文件名 -->
<SvgIcon name="cookie"/>
</template>
为之则易,不为则难。