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. Usage
<script setup lang="ts">
import { SvgIcon } from "@/components/SvgIcon";
</script>
<template>
<!-- name 为 svg icon 的文件名 -->
<SvgIcon name="cookie" />
</template>