Webpack别名在React类组件中未正常解析,动态加载音频报错
嘿,我之前碰到过完全一样的问题!核心原因是Webpack对require的静态分析机制——它在构建阶段需要明确的文件路径才能正确打包资源,动态传入路径的话,Webpack没法提前预判要处理哪些音频文件,所以就会抛出那个找不到模块的错误。
下面给你两个可行的解决方案,你可以根据自己的需求选择:
方案一:提前在数据里预加载音频(最直接简单)
既然你的音频文件是固定的几个,不如直接在characterData.js里用require把音频路径处理好,而不是存纯字符串路径:
修改api/characterData.js:
export default [ { name: 'Chewbaca', hp: 120, strength: 5, image: 'images/chewie.jpg', audio: require('audio/chewie.mp3') }, { name: 'Darth Vader', hp: 150, strength: 7, image: 'images/darth.png', audio: require('audio/darthbreath.mp3') }, { name: 'Princess Leia', hp: 160, strength: 8, image: 'images/leia.jpg', audio: require('audio/leiasound.mp3') }, { name: 'Obi Wan Kenobi', hp: 115, strength: 6, image: 'images/obiwan.png', audio: require('audio/obisound.mp3') }, ];
然后简化你的playAudio方法,因为传入的srcFile已经是Webpack处理好的可直接访问的URL了:
playAudio = srcFile => { const characterAudio = new Audio(srcFile); characterAudio.play(); }
这样Webpack在构建时就能静态解析所有require调用,把对应的音频文件打包到输出目录,动态点击播放就完全没问题了。
方案二:用Webpack上下文批量导入(适合文件较多的场景)
如果以后你可能会新增更多音频文件,不想一个个手动加require,可以用Webpack的上下文模块来批量导入audio目录下的所有音频:
首先在api/characterData.js里添加上下文导入逻辑:
// 创建audio目录的上下文,匹配所有.mp3文件 const audioContext = require.context('audio/', false, /\.mp3$/); // 生成一个文件名到音频URL的映射 const audioMap = audioContext.keys().reduce((map, key) => { // 去掉路径前缀和后缀,比如把'./chewie.mp3'转成'chewie' const fileName = key.replace('./', '').replace('.mp3', ''); map[fileName] = audioContext(key); return map; }, {}); export default [ { name: 'Chewbaca', hp: 120, strength: 5, image: 'images/chewie.jpg', audio: 'chewie' // 只存文件名(不带路径和后缀) }, { name: 'Darth Vader', hp: 150, strength: 7, image: 'images/darth.png', audio: 'darthbreath' }, // ...其他角色 ]; // 导出映射供App.js使用 export { audioMap };
然后修改App.js的playAudio方法,通过映射获取对应的音频URL:
import React, { Component } from 'react'; import styles from './App.scss'; import characterData, { audioMap } from 'api/characterData'; class App extends Component { state = { characters: [...characterData] } playAudio = audioKey => { const audioUrl = audioMap[audioKey]; if (!audioUrl) { console.error(`找不到对应音频:${audioKey}`); return; } const characterAudio = new Audio(audioUrl); characterAudio.play(); } render() { return ( <div className={styles.characterCard}> {this.state.characters.map(({ audio, name }) => { return ( <div className={styles.characterCard} key={name} onClick={() => this.playAudio(audio)} > <h3>{name}</h3> </div> ) })} </div> ); } } export default App;
这种方式Webpack会自动打包audio目录下所有.mp3文件,以后新增文件只要命名符合规则,直接在characterData里加对应的键就行,不用手动写require。
补充:为什么硬编码能正常运行?
当你硬编码require('audio/chewie.mp3')时,Webpack能明确识别这个固定路径,在构建时把音频文件复制到输出目录,并把require的返回值替换成正确的访问URL。但动态传参时,Webpack没法提前解析路径,不知道要打包哪些文件,所以就会抛出Cannot find module "."的错误——它根本不知道你要找哪个文件!
内容的提问来源于stack exchange,提问作者Kyle Holmberg

