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

React Native动态加载餐厅本地图片问题求助(无需批量导出)

餐厅卡片动态图片加载问题解决方案

问题概述

开发外卖配送布局时,餐厅名称可正常渲染,但通过JSON动态加载图片失败。硬编码图片路径(如require('@/src/assets/restaurants/kfc.png'))能正常显示,动态拼接路径则无法加载,需要无需手动批量导出图片的自动加载方案。

现有文件结构

/
|--/src
     |--/App
     |    |--index.tsx
     |--/components
     |    |--/restaurants
     |         |--index.tsx
     |         |--/restaurantCard
     |              |--index.tsx
     |--/assets
          |--/restaurants
               |--all images png

核心原因

React Native的Metro打包工具需要在编译阶段确定所有静态资源的路径,运行时动态拼接的require路径无法被解析,因此无法正确打包对应的图片资源。


解决方案

方案1:Expo项目推荐(使用expo-asset)

利用Expo的expo-asset库批量管理资源,无需手动逐个导入:

  1. 安装依赖
npx expo install expo-asset
  1. 创建资源映射文件(src/assets/restaurants/index.ts)
import { Asset } from 'expo-asset';

// 自动导入目录下所有PNG图片(Expo默认支持glob导入)
const images = require.context('@/src/assets/restaurants', false, /\.png$/);

// 生成图片名到URI的映射
const restaurantImages = Object.fromEntries(
  images.keys().map(key => {
    const imageName = key.replace('./', '');
    return [imageName, Asset.fromModule(images(key)).uri];
  })
);

export default restaurantImages;
  1. 修改RestaurantCard组件使用映射
import { View, Text, Pressable, Image } from 'react-native'
import { RestaurantsProps } from '..'
import { FontAwesome, Ionicons } from '@expo/vector-icons'
import restaurantImages from '@/src/assets/restaurants';

export default function RestaurantCard({ item }: { item: RestaurantsProps }) {
    const width = 140; 
    const height = 100;
    // 直接从映射中获取图片URI
    const Logo = restaurantImages[item.image];

    return (
        <Pressable style={{
            marginVertical: 4,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#c8c8c8',
            borderRadius: 16,
        }}
            onPress={() => { console.log(`CLICOU EM ${item.name}`) }}
        >
            <View style={{
                width: width,
                height: height,
                backgroundColor: 'black',
                borderRadius: 16,
            }}>
                <Image source={{uri: Logo}} style={{
                    width: width,
                    height: height,
                    resizeMode: 'stretch',
                    borderRadius: 16,
                }} />
            </View>
            <FontAwesome name='circle' size={38} color={'#aaa'} style={{
                position: 'absolute',
                bottom: 26,
            }} />
            <Ionicons name='fast-food-outline' size={24} style={{
                position: 'absolute',
                bottom: 32,
                color: '#ddd'
            }} />
            <Text className='font-semibold text-gray-600 text-lg mt-5'>{item.name}</Text>
        </Pressable>
    )
}

方案2:纯React Native项目(无Expo)

通过自动生成资源映射文件的方式解决,避免手动维护:

  1. 编写自动生成映射的Node脚本(generate-image-map.js)
const fs = require('fs');
const path = require('path');

// 图片目录路径
const imgDir = path.join(__dirname, 'src/assets/restaurants');
// 获取所有PNG图片文件
const imgFiles = fs.readdirSync(imgDir).filter(file => file.endsWith('.png'));

// 生成TS文件内容
const mapContent = `const images = {
  ${imgFiles.map(file => `'${file}': require('./${file}'),`).join('\n  ')}
};

export default images;`;

// 写入到图片目录的index.ts
fs.writeFileSync(path.join(imgDir, 'index.ts'), mapContent);
  1. 运行脚本生成映射文件
node generate-image-map.js

(每次新增图片后重新运行脚本即可)

  1. 修改RestaurantCard组件使用映射
import { View, Text, Pressable, Image } from 'react-native'
import { RestaurantsProps } from '..'
import { FontAwesome, Ionicons } from '@expo/vector-icons'
import images from '@/src/assets/restaurants';

export default function RestaurantCard({ item }: { item: RestaurantsProps }) {
    const width = 140; 
    const height = 100;
    // 通过映射获取资源并解析URI
    const Logo = Image.resolveAssetSource(images[item.image]).uri;

    return (
        <Pressable style={{
            marginVertical: 4,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#c8c8c8',
            borderRadius: 16,
        }}
            onPress={() => { console.log(`CLICOU EM ${item.name}`) }}
        >
            <View style={{
                width: width,
                height: height,
                backgroundColor: 'black',
                borderRadius: 16,
            }}>
                <Image source={{uri: Logo}} style={{
                    width: width,
                    height: height,
                    resizeMode: 'stretch',
                    borderRadius: 16,
                }} />
            </View>
            <FontAwesome name='circle' size={38} color={'#aaa'} style={{
                position: 'absolute',
                bottom: 26,
            }} />
            <Ionicons name='fast-food-outline' size={24} style={{
                position: 'absolute',
                bottom: 32,
                color: '#ddd'
            }} />
            <Text className='font-semibold text-gray-600 text-lg mt-5'>{item.name}</Text>
        </Pressable>
    )
}

内容的提问来源于stack exchange,提问作者Kauã

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 17:55:57