You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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组件,完整保留所有结构与样式:

  1. 配置vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  assetsInclude: ['**/*.svg']
});
  1. 创建通用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;
  1. 使用示例:
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组件,支持自定义配置保留样式:

  1. 安装插件:
npm install vite-plugin-svgr --save-dev
  1. 配置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属性
      }
    })
  ]
});
  1. 使用示例:
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文件,避免插件的过度优化:

  1. 手动创建精灵图文件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>
  1. 创建精灵图组件使用:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 08:35:07