SVGR 插件
默认情况下,Rsbuild 会将 SVG 图片当作静态资源处理,处理规则可参考:引用静态资源。
通过添加 SVGR 插件,Rsbuild 支持调用 SVGR,将 SVG 图片转换为一个 React 组件使用。
快速开始
安装插件
你可以通过如下的命令安装插件:
npm add @rsbuild/plugin-svgr -D
注册插件
你可以在 rsbuild.config.ts
文件中注册插件:
rsbuild.config.ts
import { pluginSvgr } from '@rsbuild/plugin-svgr';
export default {
plugins: [pluginSvgr()],
};
示例
默认用法
注册插件后,当你在 JS 文件中引用 SVG 资源时,如果导入的路径包含 ?react
后缀,Rsbuild 会调用 SVGR,将 SVG 图片转换为一个 React 组件。
App.jsx
import Logo from './logo.svg?react';
export const App = () => <Logo />;
如果导入的路径不包含 ?react
后缀,那么 SVG 会被当做普通的静态资源来处理,你会得到一个 URL 字符串:
import logoURL from './static/logo.svg';
console.log(logoURL); // => "/static/logo.6c12aba3.png"
具名导入
@rsbuild/plugin-svgr
支持具名导入 ReactComponent
来使用 SVGR,你需要设置 svgrOptions.exportType 为 'named'
:
pluginSvgr({
svgrOptions: {
exportType: 'named',
},
});
App.jsx
import { ReactComponent as Logo } from './logo.svg';
export const App = () => <Logo />;
@rsbuild/plugin-svgr
也支持默认导入和混合导入等用法:
选项
如果你需要自定义 SVGR 的编译行为,可以使用以下配置项:
type PluginSvgrOptions = {
/**
* 修改 SVGR 选项
*/
svgrOptions?: import('@svgr/core').Config;
/**
* 是否允许同时使用默认导入和具名导入
* @default false
*/
mixedImport?: boolean;
};
svgrOptions
用于修改 SVGR 的选项,传入的对象会与默认值进行 deep merge。完整文档请参考 SVGR - Options。
- 类型:
import('@svgr/core').Config
- 默认值:
const defaultSvgrOptions = {
svgo: true,
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
},
'prefixIds',
],
},
};
pluginSvgr({
svgrOptions: {
svgoConfig: {
datauri: 'base64',
},
},
});
当你设置 svgoConfig.plugins
时,同名 plugin 的配置会被自动合并,比如下面的配置会与内置的 preset-default
进行合并:
pluginSvgr({
svgrOptions: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
cleanupIds: false,
},
},
},
],
},
},
});
合并后的 svgoConfig
`如下:
const mergedSvgoConfig = {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: true,
cleanupIds: false,
},
},
},
'prefixIds',
],
};
svgrOptions.exportType
设置 SVG React 组件的导出方式。
- 类型:
'default' | 'named'
- 默认值:
undefined
exportType
可以设置为:
default
:使用默认导出。
named
:使用 ReactComponent
具名导出。
比如把 SVG 文件默认导出的内容设置为 React 组件:
pluginSvgr({
svgrOptions: {
exportType: 'default',
},
});
此时再使用默认导入,你会得到一个 React 组件,而不是 URL:
import Logo from './logo.svg';
console.log(Logo); // => React 组件
同时,你也可以通过指定 ?url
的 query 来导入 url,比如:
import logo from './logo.svg?url';
console.log(logo); // => 资源 url
TIP
当 svgrOptions.exportType
被设置为 'default'
时,具名导入(ReactComponent)将无法使用。
mixedImport
是否开启混合导入,允许同时使用默认导入和命名导入。
混合导入通常和 svgrOptions.exportType: 'named'
同时使用,比如:
pluginSvgr({
mixedImport: true,
svgrOptions: {
exportType: 'named',
},
});
此时引用的 SVG 文件会同时导出 URL 和 React 组件:
import logoUrl, { ReactComponent as Logo } from './logo.svg';
console.log(logoUrl); // -> string
console.log(Logo); // -> React component
局限性
建议优先使用 ?react
来将 SVG 转换为 React 组件,而不是使用混合导入。因为混合导入有如下局限性:
- 包体积增加:混合导入会导致单个 SVG 模块被编译为两种代码(即使部分导出没有被使用),这会增加产物的包体积。
- 编译速度下降:混合导入会产生额外的编译开销。即使代码中未使用到 ReactComponent 导出,SVG 文件仍然会被 SVGR 编译。而 SVGR 是基于 Babel 实现的,性能开销较大。
query
用于自定义匹配 SVGR 转换的 query 后缀。
比如需要匹配带有 ?svgr
后缀的 import 路径:
pluginSvgr({
query: /svgr/,
});
App.jsx
import Logo from './logo.svg?svgr';
export const App = () => <Logo />;
exclude
用于排除一部分 SVG 模块,这些 SVG 模块不会经过 SVGR 处理。
比如,项目中包含 a.svg
和 b.svg
,你可以将 b.svg
添加到 exclude:
pluginSvgr({
svgrOptions: {
exportType: 'default',
},
exclude: /b\.svg/,
});
在引用时,a.svg
会被转换为 React 组件,b.svg
会被当做普通的静态资源来处理:
src/index.ts
import component from './a.svg';
import url from './b.svg';
console.log(component); // => React 组件
console.log(url); // => 资源 url
excludeImporter
用于排除一部分模块,这些模块引用的 SVG 文件不会经过 SVGR 处理。
比如,项目中包含 page-a/index.ts
和 page-b/index.ts
,你可以将 page-b
添加到 excludeImporter:
pluginSvgr({
svgrOptions: {
exportType: 'default',
},
excludeImporter: /\/page-b\/index\.ts/,
});
- page-a 中引用的 SVG 会被转换为 React 组件:
page-a/index.ts
import Logo from './logo.svg';
console.log(Logo); // => React 组件
- page-b 中引用的 SVG 会被当做普通的静态资源来处理:
page-b/index.ts
import url from './logo.svg';
console.log(url); // => 资源 url
TIP
模块路径中的 query 比 exclude
和 excludeImporter
具有更高的优先级。比如某个模块被 exclude,添加 ?react
依然可以使它被 SVGR 转换。
类型声明
当你在 TypeScript 代码中引用 SVG 资源时,TypeScript 可能会提示该模块缺少类型定义:
TS2307: Cannot find module './logo.svg?react' or its corresponding type declarations.
此时你需要为 SVG 资源添加类型声明文件,请在项目中创建 src/env.d.ts
文件,并添加相应的类型声明。
declare module '*.svg' {
const content: string;
export default content;
}
declare module '*.svg?react' {
const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export default ReactComponent;
}
- 如果
svgrOptions.exportType
的值为 'default'
,则将类型声明设置为:
declare module '*.svg' {
const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export default ReactComponent;
}
declare module '*.svg?react' {
const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export default ReactComponent;
}
- 如果
svgrOptions.exportType
的值为 'named'
,则将类型声明设置为:
declare module '*.svg' {
export const ReactComponent: React.FunctionComponent<
React.SVGProps<SVGSVGElement>
>;
}
declare module '*.svg?react' {
const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export default ReactComponent;
}
- 如果
svgrOptions.exportType
的值为 'named'
,且开启了 mixedImport
,则将类型声明设置为:
declare module '*.svg' {
export const ReactComponent: React.FunctionComponent<
React.SVGProps<SVGSVGElement>
>;
const content: string;
export default content;
}
declare module '*.svg?react' {
const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export default ReactComponent;
}
添加类型声明后,如果依然存在上述错误提示,请尝试重启当前 IDE,或者调整 env.d.ts
所在的目录,使 TypeScript 能够正确识别类型定义。