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 字符串或 base64 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 能够正确识别类型定义。