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

React Vite中如何无需保存文件展示API返回的动态SVG

在React + Vite中展示API获取的SVG字符串

问题描述

我从API获取了一段SVG字符串,格式如下:

<svg width="848" height="848" viewBox="0 0 848 848" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">xxxxxxxxx</svg>

希望在React组件中展示该SVG,但不想为每个SVG创建单独的.svg文件(比如import { ReactComponent as Ticket} from "./ticket.svg";这种方式)。同时因为SVG包含直接链接/API调用等复杂内容,<img src={svg} />方法也不适用。当前已启用vite-plugin-svgr,Vite配置如下:

export default defineConfig({
  plugins: [svgr(), react()],
});

现有组件代码:

const TicketDetail = () => {
  const [ticketSvg, setTicketSvg] = useState();

  const getTicketSVG = () => {
    // ...fetching SVG here

    setTicketSvg(svg); // looks something like <svg> xxxxxxxxxxx </svg>
  };

  return (
    <header className={styles.header}>
      // display svg here
      <svg>{ticketSvg}</svg>
    </header>
  );
};

请问该如何实现需求?(可使用或不使用SVGR)


解决方案

方法1:使用dangerouslySetInnerHTML(无需SVGR)

这是最直接的实现方式,直接将SVG字符串作为HTML内容插入DOM。注意:必须确保API返回的SVG内容完全可信,避免XSS攻击风险。

修改后的组件代码:

import { useState, useEffect } from 'react';
import styles from './TicketDetail.module.css'; // 假设使用CSS Modules

const TicketDetail = () => {
  const [ticketSvg, setTicketSvg] = useState("");

  const getTicketSVG = async () => {
    // 替换为实际的API请求逻辑
    const response = await fetch('/your-api-endpoint');
    const svgContent = await response.text();
    setTicketSvg(svgContent);
  };

  // 组件挂载时触发SVG获取
  useEffect(() => {
    getTicketSVG();
  }, []);

  return (
    <header className={styles.header}>
      {/* 只有当SVG内容存在时才渲染 */}
      {ticketSvg && (
        <div dangerouslySetInnerHTML={{ __html: ticketSvg }} />
      )}
    </header>
  );
};

export default TicketDetail;

方法2:利用SVGR将SVG字符串转为React组件

如果你已经安装了vite-plugin-svgr,可以结合@svgr/core将SVG字符串直接转换为React组件,适合需要对SVG做组件化控制的场景。

  1. 先安装依赖:
npm install @svgr/core --save-dev
  1. 修改组件代码:
import { useState, useEffect } from 'react';
import { transform } from '@svgr/core';
import styles from './TicketDetail.module.css';

const TicketDetail = () => {
  const [TicketComponent, setTicketComponent] = useState(null);

  const getTicketSVG = async () => {
    const response = await fetch('/your-api-endpoint');
    const svgString = await response.text();

    // 将SVG字符串转换为React组件代码
    const componentCode = await transform(
      svgString,
      {},
      { componentName: 'Ticket' }
    );

    // 动态创建组件(仅在SVG内容可信时使用)
    const Component = eval(`(function(){return ${componentCode}})()`);
    setTicketComponent(<Component />);
  };

  useEffect(() => {
    getTicketSVG();
  }, []);

  return (
    <header className={styles.header}>
      {TicketComponent}
    </header>
  );
};

export default TicketDetail;

⚠️ 注意:eval存在安全风险,仅当你完全信任API返回的SVG内容时使用。

方法3:更安全的SVGR转换方式(避免eval)

如果不想用eval,可以结合Babel和SVGR的JSX插件来转换,安全性更高:

  1. 安装所需依赖:
npm install @svgr/core @svgr/plugin-jsx @babel/core @babel/preset-react --save-dev
  1. 组件代码:
import { useState, useEffect } from 'react';
import { transform } from '@svgr/core';
import jsxPlugin from '@svgr/plugin-jsx';
import { transformSync } from '@babel/core';
import styles from './TicketDetail.module.css';

const TicketDetail = () => {
  const [TicketComponent, setTicketComponent] = useState(null);

  const getTicketSVG = async () => {
    const response = await fetch('/your-api-endpoint');
    const svgString = await response.text();

    // 将SVG转为JSX代码
    const jsxCode = await transform(
      svgString,
      { plugins: [jsxPlugin] },
      { componentName: 'Ticket' }
    );

    // 用Babel将JSX转为可执行的React组件代码
    const { code } = transformSync(jsxCode, {
      presets: ['@babel/preset-react'],
    });

    // 通过动态导入创建组件
    const module = await import(`data:text/javascript,${code}`);
    setTicketComponent(<module.default />);
  };

  useEffect(() => {
    getTicketSVG();
  }, []);

  return (
    <header className={styles.header}>
      {TicketComponent}
    </header>
  );
};

export default TicketDetail;

内容的提问来源于stack exchange,提问作者Bram

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 19:10:09