如何优化React/React Native自定义SVG组件,仅加载使用的SVG
问题描述
我正在创建一个自定义分类组件,会加载对象中列出的所有SVG,代码实现如下:
import { type SvgProps } from 'react-native-svg'; import FoodAndDrink from './icon-svgs/food-and-drink.svg'; import TransportOrFuel from './icon-svgs/transport-or-fuel.svg'; const Svgs = { foodAndDrink: FoodAndDrink, transportOrFueld: TransportOrFuel, }; export type CategoryIconProps = { name: keyof typeof Svgs; size?: number; }; export const CategoryIcon = ({ name, size, ...props }: SvgProps & CategoryIconProps) => { const Component = Svgs[name]; return <Component {...(size && { width: size, height: size })} {...props} />; };
这个实现能正常运行,但我担心随着SVG数量增加会影响性能——哪怕我只调用单个SVG:
<CategoryIcon name="foodAndDrink" size={horizontalScale(32)} />
未被使用的SVG依然会被加载。有没有更优方案能确保只加载选中的SVG?
我试过用动态导入,但因为React Native组件不能是异步函数,所以这个方案行不通,代码如下:
export const CategoryIcon = async ({ name, size, ...props }: SvgProps & CategoryIconProps) => { const Svgs = { foodAndDrink: (await import('./icon-svgs/food-and-drink.svg')).default, // ... 其他SVG同理定义 }; const Component = Svgs[name]; return <Component {...(size && { width: size, height: size })} {...props} />; };
解决方案
方法1:组件内动态导入+状态管理
React Native不允许组件本身是异步函数,但可以在组件内部通过useEffect配合状态实现按需加载:
import { useState, useEffect } from 'react'; import { type SvgProps } from 'react-native-svg'; export type CategoryIconProps = { name: 'foodAndDrink' | 'transportOrFuel'; size?: number; }; export const CategoryIcon = ({ name, size, ...props }: SvgProps & CategoryIconProps) => { const [IconComponent, setIconComponent] = useState<React.FC<SvgProps> | null>(null); useEffect(() => { let isMounted = true; const loadIcon = async () => { let module; switch(name) { case 'foodAndDrink': module = await import('./icon-svgs/food-and-drink.svg'); break; case 'transportOrFuel': module = await import('./icon-svgs/transport-or-fuel.svg'); break; default: module = null; } if (isMounted && module) { setIconComponent(module.default); } }; loadIcon(); return () => { isMounted = false; }; }, [name]); if (!IconComponent) { // 可替换为自定义加载占位符,比如圆形加载动画 return null; } return <IconComponent {...(size && { width: size, height: size })} {...props} />; };
- 核心逻辑:在组件挂载或
name参数变化时,动态导入对应SVG组件,通过状态保存后渲染 - 加入
isMounted标记,避免组件卸载后更新状态引发警告 - 可根据需求自定义加载状态,优化用户体验
方法2:封装导入映射+按需加载
把导入逻辑封装成单独函数,让代码更整洁,同时配合Metro的树摇能力实现按需打包:
import { useState, useEffect } from 'react'; import { type SvgProps } from 'react-native-svg'; // 定义SVG组件的导入映射 const iconImports = { foodAndDrink: () => import('./icon-svgs/food-and-drink.svg'), transportOrFuel: () => import('./icon-svgs/transport-or-fuel.svg'), }; export type CategoryIconProps = { name: keyof typeof iconImports; size?: number; }; export const CategoryIcon = ({ name, size, ...props }: SvgProps & CategoryIconProps) => { const [IconComponent, setIconComponent] = useState<React.FC<SvgProps> | null>(null); useEffect(() => { let isMounted = true; iconImports[name]().then(module => { if (isMounted) { setIconComponent(module.default); } }); return () => { isMounted = false; }; }, [name]); if (!IconComponent) { return null; } return <IconComponent {...(size && { width: size, height: size })} {...props} />; };
- 这种方式把导入逻辑和组件渲染逻辑分离,便于后续维护
- Metro会识别动态导入语法,仅打包实际用到的SVG文件,避免冗余加载
注意事项
- 动态导入会有短暂加载延迟,建议配合加载占位符使用
- 确保React Native项目配置支持SVG动态导入(现代RN版本默认支持)
- TypeScript类型定义要覆盖所有SVG名称,避免类型报错
内容的提问来源于stack exchange,提问作者Bryan Lumbantobing
相关产品推荐
相关产品推荐

