如何使用vite-plugin-svgr为SVG添加<title>子元素(无需使用ref)?
解决vite-plugin-svgr添加SVG 子元素的问题 <a class="header-anchor" href="#解决vite-plugin-svgr添加svg-子元素的问题" aria-hidden="true">#</a></h1>
<p>你需要给SVG添加<code><title></code>子元素而非SVG标签的<code>title</code>属性,以下是两种无需使用ref的可行方案:</p>
<h2 id="方案一:修改vite-plugin-svgr配置,全局支持title-prop">方案一:修改vite-plugin-svgr配置,全局支持title prop <a class="header-anchor" href="#方案一:修改vite-plugin-svgr配置,全局支持title-prop" aria-hidden="true">#</a></h2>
<p>通过自定义svgr的组件模板,让所有导入的SVG组件自动支持<code>title</code>参数,并将其渲染为第一个子元素:</p>
<p>在<code>vite.config.js</code>中配置:</p>
<pre class="hljs"><code class="language-javascript volc-pre-code">import { defineConfig } from 'vite';
import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [
svgr({
svgrOptions: {
template: (variables, { tpl }) => {
return tpl`
${variables.imports};
${variables.interfaces};
const ${variables.componentName} = (${variables.props}) => {
const { title, ...restProps } = ${variables.props};
return (
<svg {...restProps}>
{title && <title>{title}</title>}
${variables.children}
</svg>
);
};
export default ${variables.componentName};
`;
},
},
}),
],
});
</code></pre>
<p>配置完成后,直接给SVG组件传<code>title</code>即可:</p>
<pre class="hljs"><code class="language-jsx volc-pre-code">import { ReactComponent as Circle } from "./assets/circle.svg";
const Icon = () => {
return (
<div>
<Circle title="icon title" />
</div>
);
};
</code></pre>
<p>最终生成的DOM结构:</p>
<pre class="hljs"><code class="language-html volc-pre-code"><svg height="100" width="100" xmlns="http://www.w3.org/2000/svg">
<title>icon title</title>
<circle r="45" cx="50" cy="50" fill="red"></circle>
</svg>
</code></pre>
<h2 id="方案二:封装高阶组件(hoc),单个组件处理">方案二:封装高阶组件(HOC),单个组件处理 <a class="header-anchor" href="#方案二:封装高阶组件(hoc),单个组件处理" aria-hidden="true">#</a></h2>
<p>如果不想修改全局配置,可以封装一个高阶组件,给目标SVG组件插入<code><title></code>元素:</p>
<pre class="hljs"><code class="language-jsx volc-pre-code">import React from 'react';
const withSvgTitle = (SvgComponent) => {
return ({ title, ...props }) => {
const svgElement = <SvgComponent {...props} />;
// 保留原SVG的子元素,同时插入title作为第一个子元素
return React.cloneElement(svgElement, {}, [
title && <title key="svg-title">{title}</title>,
...React.Children.toArray(svgElement.props.children)
]);
};
};
</code></pre>
<p>使用方式:</p>
<pre class="hljs"><code class="language-jsx volc-pre-code">import { ReactComponent as Circle } from "./assets/circle.svg";
// 包装目标SVG组件
const CircleWithTitle = withSvgTitle(Circle);
const Icon = () => {
return (
<div>
<CircleWithTitle title="icon title" />
</div>
);
};
</code></pre>
<h3 id="为什么你之前的方法无效?">为什么你之前的方法无效? <a class="header-anchor" href="#为什么你之前的方法无效?" aria-hidden="true">#</a></h3>
<ul>
<li>直接传<code>title="icon title"</code>:vite-plugin-svgr会将其作为SVG标签的属性,而非生成<code><title></code>子元素;</li>
<li>你的<code>cloneElement</code>写法:直接替换了原SVG组件的所有子元素,导致原有的<code><circle></code>丢失,正确做法是保留原组件的子元素并插入title。</li>
</ul>
<p>内容的提问来源于stack exchange,提问作者netanel</p>
相关产品推荐
相关产品推荐

