我正在解决一个需要将 SVG 图像从 URL(AWS S3) 加载到 React 组件的问题。我能够使用本地文件中的 SVG 内联react组件成功显示和加载图像。但是,svg 文件需要从 S3 存储桶内联加载,JS svg 导入不适用于 URL。
所以我试图为此找到最好的替代解决方案?
我正在解决一个需要将 SVG 图像从 URL(AWS S3) 加载到 React 组件的问题。我能够使用本地文件中的 SVG 内联react组件成功显示和加载图像。但是,svg 文件需要从 S3 存储桶内联加载,JS svg 导入不适用于 URL。
所以我试图为此找到最好的替代解决方案?
@ivnkld 的回答为我指明了正确的方向,谢谢!对于像我这样的 Google 员工,这里是使用钩子实现相同方法的:
import React, { useEffect, useState } from 'react';
const SvgInline = props => {
const [svg, setSvg] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [isErrored, setIsErrored] = useState(false);
useEffect(() => {
fetch(props.url)
.then(res => res.text())
.then(setSvg)
.catch(setIsErrored)
.then(() => setIsLoaded(true))
}, [props.url]);
return (
<div
className={`svgInline svgInline--${isLoaded ? 'loaded' : 'loading'} ${isErrored ? 'svgInline--errored' : ''}`}
dangerouslySetInnerHTML={{ __html: svg }}
/>
);
}
您应该获取 svg 图像并将其粘贴到 html 中
const SVG_URL = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/tiger.svg';
class Svg extends React.Component {
state = {
svg: null,
loading: false,
}
componentDidMount() {
fetch(this.props.url)
.then(res => res.text())
.then(text => this.setState({ svg: text }));
}
render() {
const { loading, svg } = this.state;
if (loading) {
return <div className="spinner"/>;
} else if(!svg) {
return <div className="error"/>
}
return <div dangerouslySetInnerHTML={{ __html: this.state.svg}}/>;
}
}
ReactDOM.render(
<Svg url='https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/tiger.svg' />,
document.querySelector("#app")
);
您可以在此处编辑示例https://jsfiddle.net/anu6x3bk/
React 不推荐使用,dangerouslySetInnerHTML
所以你应该使用一些像svg-inline-react
对于那些想要避免使用危险的SetInnerHTML 的人,请查看以下示例。一旦我们从 api 获取 svg 作为文本,我们将使用DOMParser将文本转换为Document对象。然后我们将使用React.createElement来渲染图像(或)图标。
// utils/convertDocEleToReact.js
import { createElement } from "react";
const innerFunction = (element, props) => {
const tagName = element.tagName;
let _props = props || {};
for(let i = 0; i < element.attributes.length; i++){
_props[element.attributes[i].nodeName] = element.attributes[i].nodeValue;
}
let children = Array.from(element.children).map(item => innerFunction(item));
return createElement(tagName, _props, children);
};
export const convertDocEleToReact = (element, props) => {
try{
return innerFunction(element, props);
}
catch(ex){
return createElement("span", {}, "Error loading svg image");
}
};
// components/SVGUrl.js
import React from "react";
import { convertDocEleToReact } from "./utils/convertDocEleToReact";
export const SVGUrl = ({ ...props }) => {
const [ Comp, setComp ] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(_ => {
setLoading(true);
fetch(/** svg url */)
.then(res => res.text())
.then(res => {
const domParser = new DOMParser();
const ele = domParser.parseFromString(res, "image/svg+xml");
setComp(convertDocEleToReact(ele.documentElement, props));
setLoading(false);
});
}, []);
return loading ? "..." : Comp;
};