1、使用自带标签进行渲染
可以使用img、embed、picture、object等标签渲染,要注意地址的跨域,这种方法比较简单,但是没办法对拿到的svg进行操作,比如进行主题模式切换的同时切换svg颜色,由于svg中包含有fill-color等字段,设置style很难生效。当然,后端也可以返回两种不同主题的地址。
<img src={url} />
<embed src={url} />
<picture>
<source srcSet={url}></source>
</picture>
<object
data={url}
style={{ width: '25px', height: '25px' }}
type="image/svg+xml"
/>
2、获取svg地址内容,使用div的innerhtml进行渲染
可以先通过请求(fetch或者xmlrequest)获取到文件内容,但由于拿到的内容是字符串,不能直接当做节点渲染,所以可以使用div自带的输属性innerhtml进行渲染。这样做是因为SVG是XML格式的,可以直接作为HTML的一部分被渲染。
这样做的好处是可以对字符串进行一些修改,比如使用replace进行某些字段的匹配修改,但是对于不同的svg的格式要求可能相对严格,不适合封装为组件。
但是要注意这样的安全性,直接将不明字符串渲染到页面可能会导致XSS(跨站点脚本)攻击,所以要确保是安全的。
<div
dangerouslySetInnerHTML={{
__html: svgString,
}}
></div>
3、获取svg地址内容,转化为节点进行渲染
获取到内容之后,将字符串转为DOM元素,根据DOM元素丰富的属性可以很大程度上更改svg的颜色,但是要注意React不能直接渲染Html片段,需要转为React元素才能渲染,转换之后path可能wei空,所以需要特殊处理path的d属性
export const EuSvgUrlIcon: FC<EuUrlIconProps> = ({ url }) => {
const [svg, setSvg] = useState<any>();
const elementFromNode = (node: Node): JSX.Element | null => {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element;
const children = Array.from(element.childNodes || []).map(
elementFromNode,
);
const attributes: any = {};
Array.from(element.attributes).forEach((attr) => {
attributes[attr.name] = attr.value;
});
// 特别处理<path>元素的情况,将d属性转换为children
if (element.tagName.toLowerCase() === 'path') {
const d = element.getAttribute('d');
if (d) {
children.push(React.createElement('path', { d }));
}
}
return React.createElement(
element.tagName.toLowerCase(),
attributes,
...children,
);
} else if (node.nodeType === Node.TEXT_NODE) {
return node.nodeValue
? React.createElement(React.Fragment, {}, node.nodeValue)
: null;
} else {
return null;
}
};
useEffect(() => {
//获取url内容的请求函数,
url2svg(url).then((res) => {
const svgNode = new DOMParser().parseFromString(
res,
'image/svg+xml',
).documentElement;
const jsxSvg =
svgNode && svgNode.nodeName === 'svg' ? elementFromNode(svgNode) : null;
console.log(res, svgNode.nodeName, jsxSvg);
setSvg(jsxSvg);
// setSvg(createElement(res || ''));
});
// new DOMParser().parseFromString(svg, 'image/svg+xml').documentElement
}, [url]);
return (
<>
{/* <div
dangerouslySetInnerHTML={{
__html: svg,
}}
></div> */}
{svg}
</>
);
};