Vite中React TS组件加载带分组样式的SVG Sprite问题
问题描述
我使用SVG精灵图加载图标,示例代码如下:
<svg aria-hidden="true" class="block w-full h-full block"><use xlink:href="/src/assets/sprite.svg?t=123#triangle"></use></svg>
加载后Shadow DOM中的triangle SVG变为:
<svg viewBox="0 0 1920 1080" fill="currentColor" id="battery_distributor"><path d="M150 5 75 200h150Z"></path></svg>
但原始triangle SVG的定义是:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1920 1080"> <path d="M150 5 L75 200 L225 200 Z" style="fill: none; stroke: green; stroke-width: 3" /> </svg>
通过精灵图加载后图标显示为黑色填充,推测是vite-svg-sprite-wrapper插件(依赖SVG Sprite工具与svgo)移除了style属性。
现需在Vite + React TypeScript环境中找到替代方案,要求能加载单个SVG文件,同时支持<g>分组上的自定义样式属性。实际使用的SVG结构如下,其中带fill的分组子元素因color: transparent无法正常显示:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1920 1080"> <g> <g transform="matrix(1,0,0,1,200,500)" opacity="1" style="display: block;"> <g opacity="1" transform="matrix(1,0,0,1,0,0)"> <path stroke-linecap="round" stroke-linejoin="round" fill-opacity="0" stroke-dasharray="10" stroke="rgb(0,0,0)" stroke-opacity="0.35" stroke-width="3" d="M-20, 100"> </path> </g> <g opacity="1" transform="matrix(1,0,0,1,0,0)"> <path stroke-linecap="round" stroke-linejoin="round" fill-opacity="0" stroke-dasharray="10" stroke="rgb(180,211,46)" stroke-opacity="1" stroke-width="3" d="M-20, 100"> </path> </g> </g> <g transform="matrix(1,0,0,1,1,0)" opacity="1" style="display: block;"> <g opacity="1" transform="matrix(1,0,0,1,1,0)"> <path fill="rgb(0,0,0)" fill-opacity="0.35" d="M1, -28z"> </path> </g> <g opacity="1" transform="matrix(0, 0, 1, 1)"> <path fill="rgb(180,211,46)" fill-opacity="1" d="M1, -28z"> </path> </g> </g> </g> </svg>
解决方案
方案1:禁用svgo的样式移除规则
针对vite-svg-sprite-wrapper的svgo配置,关闭会移除样式属性的优化规则,保留原始SVG的样式定义:
在vite.config.ts中修改配置:
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import svgSpriteWrapper from 'vite-svg-sprite-wrapper'; export default defineConfig({ plugins: [ react(), svgSpriteWrapper({ svgo: { plugins: [ { removeStyleElement: false }, // 禁止移除style标签 { convertStyleToAttrs: false }, // 禁止将style属性转为标签属性 { removeAttrs: false } // 禁止移除未使用的属性 ] } }) ] });
方案2:直接将SVG作为React组件导入
利用Vite的原生能力,将SVG导入为React组件,完整保留所有结构与样式:
- 配置
vite.config.ts:
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], assetsInclude: ['**/*.svg'] });
- 创建通用SVG组件
SvgIcon.tsx:
import React, { Suspense } from 'react'; interface SvgIconProps extends React.SVGProps<SVGSVGElement> { src: string; } const SvgIcon: React.FC<SvgIconProps> = ({ src, ...props }) => { const Icon = React.lazy(() => import(`../assets/${src}`).then(module => ({ default: module.ReactComponent })) ); return ( <Suspense fallback={<div className="w-full h-full bg-gray-100" />}> <Icon {...props} /> </Suspense> ); }; export default SvgIcon;
- 使用示例:
import SvgIcon from './SvgIcon'; const App = () => { return ( <div className="p-4"> <SvgIcon src="triangle.svg" className="w-16 h-16" /> </div> ); };
方案3:使用vite-plugin-svgr插件
基于svgr的Vite插件,可将SVG转换为React组件,支持自定义配置保留样式:
- 安装插件:
npm install vite-plugin-svgr --save-dev
- 配置
vite.config.ts:
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import svgr from 'vite-plugin-svgr'; export default defineConfig({ plugins: [ react(), svgr({ svgrOptions: { removeViewBox: false, convertStyleToAttrs: false // 保留style属性 } }) ] });
- 使用示例:
import { ReactComponent as TriangleIcon } from './assets/triangle.svg'; const App = () => { return ( <div className="p-4"> <TriangleIcon className="w-16 h-16" /> </div> ); };
方案4:手动构建SVG精灵图
若坚持使用精灵图,可手动合并SVG文件,避免插件的过度优化:
- 手动创建精灵图文件
public/sprite.svg:
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="triangle" viewBox="0 0 1920 1080"> <path d="M150 5 L75 200 L225 200 Z" style="fill: none; stroke: green; stroke-width: 3" /> </symbol> <!-- 添加其他图标symbol --> </svg>
- 创建精灵图组件使用:
interface SpriteIconProps { id: string; className?: string; } const SpriteIcon: React.FC<SpriteIconProps> = ({ id, className }) => ( <svg aria-hidden="true" className={className}> <use href={`/sprite.svg#${id}`} /> </svg> ); // 使用示例 <SpriteIcon id="triangle" className="w-full h-full" />
内容的提问来源于stack exchange,提问作者dan2k3k4
相关产品推荐
相关产品推荐

