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

Expo应用中使用require导入音频文件报错求助

React Native 动态加载音频文件报错解决方案

问题详情

调用自定义Hook useAudioPlayer 传入音频文件名参数时出现报错:

error: src\components\Functions.js: src\components\Functions.js:Invalid call at line 72: require("../../assets/" + audioName)

自定义Hook代码:

export const useAudioPlayer = (audioName) => {
    const [sound, setSound] = useState();
    
    async function playSound() {
            const { sound } = await Audio.Sound.createAsync(require(`../../assets/${audioName}`));
      setSound(sound);
    await sound.playAsync();
  }

  useEffect(() => {
    return sound
      ? () => {
          sound.unloadAsync();
        }
      : undefined;
  }, [sound]);

  return [playSound];
}

组件调用方式:

const [ playSound ] = useAudioPlayer('pop.mp3');

const HandlePress = () => {
  playSound()   
}

硬编码路径可正常运行:

const { sound } = await Audio.Sound.createAsync(require(`../../assets/pop.mp3`));

已尝试两种无效方案:

  • 字符串拼接路径:
const { sound } = await Audio.Sound.createAsync(require("../../assets/" + audioName));
  • 使用babel-plugin-module-resolver:
const { sound } = await Audio.Sound.createAsync(require(`@assets/${audioName}`));

问题根源

React Native 的 require 是静态编译时分析的,打包工具需要在编译阶段确定所有资源的位置,动态拼接的路径无法被正确解析,因此会抛出无效调用的错误。

可行解决方案

方案1:预定义音频资源映射表

提前将所有需要使用的音频文件通过require导入,创建一个键值对映射表,Hook内部根据传入的文件名匹配对应的资源:

// 预定义所有需要用到的音频资源
const audioAssetsMap = {
  'pop.mp3': require('../../assets/pop.mp3'),
  'click.mp3': require('../../assets/click.mp3'),
  // 按需添加其他音频文件
};

export const useAudioPlayer = (audioName) => {
    const [sound, setSound] = useState();
    
    async function playSound() {
      // 从映射表中获取对应资源,不存在则抛出错误
      const audioSource = audioAssetsMap[audioName];
      if (!audioSource) {
        console.error(`未找到音频文件:${audioName}`);
        return;
      }
      
      const { sound } = await Audio.Sound.createAsync(audioSource);
      setSound(sound);
      await sound.playAsync();
    }

    useEffect(() => {
      return sound
        ? () => {
            sound.unloadAsync();
          }
        : undefined;
    }, [sound]);

    return [playSound];
}

方案2:直接传入已加载的资源

修改Hook的参数类型,让调用者直接传入require后的资源对象,而非文件名:

// 修改后的自定义Hook
export const useAudioPlayer = (audioSource) => {
    const [sound, setSound] = useState();
    
    async function playSound() {
      const { sound } = await Audio.Sound.createAsync(audioSource);
      setSound(sound);
      await sound.playAsync();
    }

    useEffect(() => {
      return sound
        ? () => {
            sound.unloadAsync();
          }
        : undefined;
    }, [sound]);

    return [playSound];
}

组件调用时直接传入资源:

const [ playSound ] = useAudioPlayer(require('../../assets/pop.mp3'));

const HandlePress = () => {
  playSound()   
}

内容的提问来源于stack exchange,提问作者Mahmoud Hlal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 08:25:26