我能否使用动态import()表达式将SVG文件导入为React组件?

7

从stackoverflow上的这篇答案中,我得到了一种解决方法,可以将svg作为ReactComponent导入并更改它们的颜色/宽度等。

但是对于动态导入是否也能做到同样的效果呢? 我的函数式组件:

import * as React from 'react';
import SvgIconComponent from './SvgIcon.Interface';

import {ReactComponent} from '*.svg';

const SvgIcon: React.FC<SvgIconComponent> =({width, color, name}) =>
{
    import(`../../assets/icons/${name}.svg`).then((Icon) => {
        return <Icon fill={color} width={width}/>
    });
};

export default SvgIcon;

在当前的实现中,我遇到了错误:

TS2749: 'ReactComponent' refers to a value, but is being used as a type here. // .then((Icon as ReactComponent)

TS2604: JSX element type 'Icon' does not have any construct or call signatures. // .then(Icon)

错误表示没有名为Icon的HTML属性。您可以尝试使用<i>或<span>。 - Sachin Vishwakarma
5个回答

0
从我所看到的情况来看,您的问题是导入整个文件并尝试将其呈现为组件,您需要将键/组件标识符/常量名称附加到 require 上。例如,如果您使用 export default YourSVGComponent,则可以通过以下方式动态访问它;
import * as React from 'react';
import SvgIconComponent from './SvgIcon.Interface';

const SvgIcon: React.FC<SvgIconComponent> =({width, color, name}) =>
{
    const Icon = require(`../../assets/icons/${name}.svg`).default
    return <Icon fill={color} width={width}/>
};

export default SvgIcon;

0

这里有一些错误。

'ReactComponent' 引用了一个值,但在此处被用作类型。

你在链接的答案中看到的 ReactComponent 实际上是在通过 Create React App 加载 .svg 文件时的一个属性。这是一个非常棒的功能,我今天第一次学习到它!

在那个 示例代码 中。

import { ReactComponent as Logo } from './logo.svg';

他们正在做的是从'./logo.svg'导入中获取ReactComponent属性并将其重命名为Logo
你的import {ReactComponent} from '*.svg';没有太多意义。
当使用ReactComponent作为类型时,会出现“类型作为值”的错误,因为它不是一种类型。你试图用.then((Icon as ReactComponent)进行类型断言,应该改为.then((Icon as React.ComponentType<SomePropsType>)

JSX元素类型'Icon'没有任何构造或调用签名。

让我们花点时间看看我们实际从import语句中得到了什么。
import(`../../assets/icons/${name}.svg`).then(console.log);

你应该看到一个具有三个属性的对象:ReactComponentdefaultUrl
{
  ReactComponent: ƒ SvgLogo(),
  default: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0i...",
  Url: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0i..."
}

default 导入的 svg 文件(也是 Url 属性)是图像的 data-url。您可以在 imgsrc 属性中使用它。

ReactComponent 是我们需要的属性!这就是 Create React App 将 svg 转换为 React 组件的地方。(注意:名称“SvgLogo”取决于文件名)。

从渲染中未返回任何内容。

您的 SvgIcon 函数没有返回任何内容。您的 return 语句是 then 回调的返回值。

处理异步 import 有点棘手,因为您在第一次渲染时不会得到值。在这种情况下,我们可以返回 null(或者您可能想返回一个大小占位符)。

在这里,我使用 element 状态来存储返回的元素。它最初为 null,在加载后被替换为 <Icon/>

我正在使用依赖于您的props的useEffect内部调用import。一旦导入解析完成,我们就可以从res.ReactComponent获取Icon

Icon的推断类型是any,这对您来说可能没问题。如果您想要准确的类型,可以使用混乱的as React.ComponentType<JSX.IntrinsicElements['svg']>;,它表示“这是一个React组件,它接受<svg>元素的所有props”。

import * as React from "react";

interface SVGIconProps {
  width: string | number;
  color: string;
  name: string;
}

const SvgIcon: React.FC<SVGIconProps> = ({ width, color, name }) => {
  const [element, setElement] = React.useState<JSX.Element | null>(null);

  React.useEffect(() => {
    import(`./icons/${name}.svg`).then((res) => {
      const Icon = res.ReactComponent as React.ComponentType<JSX.IntrinsicElements['svg']>;
      setElement(<Icon fill={color} width={width} />);
    });
  }, [name, color, width]);

  return element;
};

export default SvgIcon;

0

我建议您使用process.env.PUBLIC_URL来渲染您的SVG。将所有SVG文件放在public文件夹中,比如public/svgFiles

然后可以这样使用它:<img src={process.env.PUBLIC_URL + "/svgFiles/" + file_name} alt="project" />

由于从公共文件夹中获取的所有内容都包含在最终的build中,因此所有SVG文件都会被处理,您使用的路径也会被处理,因为所有内容将从公共文件夹引用,所以构建后路径不会改变。


方法2

以这种方式将SVG作为React组件导入:

import { ReactComponent as MY_SVG_COMPONENT } from '/path/to/svg'

现在您可以传递SVGComponent中可用的所有属性,例如

<MY_SVG_COMPONENT className="my-svg-class" />

在厌倦了与Typescript错误的斗争之后,我选择了这个解决方案。绕过拜占庭式。Create-react-app文档中有关于public文件夹的内容在这里 - Danny Armstrong
你可以尝试更新后的答案,通过将SVG作为React组件导入 -- import { ReactComponent as MY_SVG_COMPONENT } from '/path/to/svg' - Aakash Jha

-1

我认为使用React组件来制作颜色和宽度的动态图标是更好的方法。

以下是我的示例代码:

export const TwitterIcon = ({color, width}: Props) => (
    <svg
        width={width ? width : 24}
        height={width ? width : 24}
        viewBox="0 0 24 24"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
    >
        <path
            fill={color ? color : "#1f1f1f"}
            d="M8.0798 19.9998C9.55849 20.0493 11.0321 19.8018 12.4135 19.2721C13.795 18.7424 15.0562 17.9411 16.1227 16.9156C17.1892 15.8901 18.0393 14.6613 18.6228 13.3017C19.2063 11.9421 19.5113 10.4793 19.5198 8.9998C20.1974 8.16121 20.7006 7.19563 20.9998 6.1598C21.0221 6.07796 21.0203 5.99141 20.9946 5.91057C20.9689 5.82974 20.9203 5.75806 20.8548 5.70417C20.7893 5.65027 20.7096 5.61647 20.6253 5.60683C20.5411 5.59718 20.4558 5.6121 20.3798 5.6498C20.0253 5.82045 19.6262 5.87558 19.2386 5.80741C18.8511 5.73923 18.4948 5.5512 18.2198 5.2698C17.8687 4.88538 17.4441 4.57533 16.971 4.35803C16.498 4.14072 15.9861 4.02059 15.4657 4.00473C14.9454 3.98888 14.4272 4.07763 13.9417 4.26574C13.4563 4.45384 13.0136 4.73746 12.6398 5.0998C12.128 5.59546 11.7531 6.21509 11.5516 6.89847C11.3501 7.58186 11.3288 8.30575 11.4898 8.9998C8.1398 9.1998 5.8398 7.6098 3.9998 5.4298C3.94452 5.3672 3.87221 5.32205 3.7917 5.29987C3.71119 5.27769 3.62596 5.27943 3.54642 5.30488C3.46688 5.33033 3.39648 5.37839 3.3438 5.4432C3.29113 5.508 3.25846 5.58674 3.2498 5.6698C2.89927 7.61422 3.15213 9.61935 3.97442 11.4159C4.7967 13.2124 6.14904 14.7143 7.8498 15.7198C6.70943 17.0276 5.10801 17.8445 3.3798 17.9998C3.28721 18.0152 3.20174 18.0591 3.13535 18.1254C3.06896 18.1917 3.02497 18.2772 3.00954 18.3698C2.99411 18.4623 3.00801 18.5574 3.0493 18.6417C3.09059 18.726 3.15719 18.7952 3.2398 18.8398C4.74332 19.5912 6.39903 19.988 8.0798 19.9998Z"
        />
    </svg>
);

如果你想要制作图标库

interface IconProps {
   keyword: string; //make the clear type to make switch
   color: string;
   width: number;
}

const SvgIcon = ({keyword, color, width}: IconProps) => {
   // In this case you have to think about the switch and types in typescript.
    return (
       {
           'twitter': <TwitterIcon color={color} width={width}/>,
           'facebook': <FacebookIcon color={color} width={width}/>,
       }[keyword]
   )
}

//in component
<SvgIcon keyword='twitter' width={24} color='red'/>

如果你有太多的SVG图标,我目前手头上没有任何解决方案。如果我有任何好的想法,我会再次在这里发布。


这并没有回答如何动态导入SVG,我想这正是为了避免你所做的事情。 - Derek Henderson

-3
import MyIcon from '../../assets/icons/${name}.svg'

<MyIcon />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接