You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:35:23