React项目中expose-loader暴露对象嵌套于default属性问题咨询
你遇到的这个问题,核心原因是你的allRenderings.js是一个ES模块(因为它使用了import语句),而webpack在处理ES模块的导出时,会将export default(或者你混用的module.exports)的内容包裹在模块对象的default属性下。
当你用expose-loader暴露这个模块时,全局变量Renderings指向的是整个ES模块的命名空间对象,而你实际需要的renderings对象(包含App、Hello、World)被放在了这个命名空间的default属性里,所以必须通过Renderings.default才能访问。
下面提供几种可行的解决方法,你可以根据自己的项目习惯选择:
方法1:修改allRenderings.js为纯CommonJS模块
既然你想用module.exports导出普通对象,那可以把文件里的import改成require,让它成为标准的CommonJS模块,这样webpack就不会给它加上default包装:
修改后的allRenderings.js:
// 改用require引入ES模块,注意要取default属性 const App = require('../src/app').default; const Hello = require('../src/hello').default; const World = require('../src/world').default; let renderings = {} function setupRendering(key, render){ renderings[key] = render } setupRendering('App', App) setupRendering('Hello', Hello) setupRendering('World', World) module.exports = renderings
这样修改后,expose-loader暴露的全局Renderings就直接是你定义的renderings对象,你可以直接用Renderings.App来访问组件了。
方法2:让expose-loader直接暴露模块的default属性
如果你不想修改allRenderings.js的模块类型,可以调整expose-loader的用法,让它直接把模块的default属性暴露为全局变量Renderings。
方式A:在index.js中手动处理
把原来的require('expose-loader?Renderings!../rendering/allRenderings')替换成:
// 先引入模块,取出default属性,再挂载到全局 const renderings = require('../rendering/allRenderings').default; window.Renderings = renderings;
方式B:在webpack配置中配置expose-loader
在webpack.config.js的module.rules里添加一条规则,专门处理allRenderings.js:
module: { rules: [ // 你的babel-loader规则... { test: require.resolve('../rendering/allRenderings'), use: [ { loader: 'expose-loader', options: { exposes: { globalName: 'Renderings', moduleLocalName: 'default' // 指定暴露模块的default属性 } } } ] } ] }
然后把index.js里的require('expose-loader?Renderings!../rendering/allRenderings')删掉,这样webpack会自动帮你把allRenderings的default属性暴露为全局的Renderings。
方法3:改用ES模块的命名导出
如果你想彻底用ES模块规范,可以修改allRenderings.js的导出方式,直接命名导出组件:
修改后的allRenderings.js:
import App from '../src/app' import Hello from '../src/hello' import World from '../src/world' // 直接命名导出所有组件 export { App, Hello, World }
然后在index.js里用expose-loader暴露后,全局的Renderings就会直接包含App、Hello、World属性,不需要再通过default访问。
不管用哪种方法,修改后你都可以在index.cshtml里直接这样写:
ReactDOM.render(React.createElement(Renderings.App, { id: 'testApp' }), document.getElementById('jumbotron'))
内容的提问来源于stack exchange,提问作者BaconPancakes

