如何在单个HTML页面中加载多个WebAssembly生成的JS脚本并正常调用?
如何在单个HTML页面中加载多个WebAssembly生成的JS脚本并正常调用?
我来帮你搞定这个问题!你遇到的核心麻烦是,Emscripten默认生成的JS脚本都会去用全局的Module对象,两个脚本同时加载时就会互相干扰,导致初始化冲突,才会弹出那些错误提示。下面给你两种靠谱的解决办法,优先推荐第一种哦:
方案一:给每个WASM模块创建独立的实例(推荐)
Emscripten支持通过编译参数让生成的JS脚本变成模块化的工厂函数,这样每个模块都有自己独立的实例,不会再抢全局对象了。
1. 重新编译第二个WASM项目
编译的时候加上-s MODULARIZE=1 -s EXPORT_NAME='createHello2Module'这两个参数,比如:
emcc your_second_code.c -o hello2.js -s MODULARIZE=1 -s EXPORT_NAME='createHello2Module'
这样生成的hello2.js会暴露一个叫createHello2Module的函数,调用它就能得到一个独立的模块实例,完全不会影响第一个模块的全局Module。
2. 在HTML中使用独立实例
修改你的HTML代码,用工厂函数初始化第二个模块:
<!-- 第一个模块:保持原来的全局Module用法 --> <script src="hello.js"></script> <script> var js_wrapped_fib = Module.cwrap("fib", "number", ["number"]); function pressBtn(){ console.log("The result of fib(5) is:", js_wrapped_fib(5)); } </script> <button onclick="pressBtn()">Click me!</button> <!-- 第二个模块:使用独立的模块化实例 --> <script src="hello2.js"></script> <script> // 初始化第二个模块,因为是异步加载WASM,所以用then等待完成 let hello2Module; createHello2Module().then((moduleInstance) => { hello2Module = moduleInstance; console.log("第二个模块加载完成啦!"); }); function pressBtn2(){ if (!hello2Module) { console.log("别急,第二个模块还在加载中呢!"); return; } const js_wrapped_printout = hello2Module.cwrap("printout", "number", ["number"]); console.log("The clicked button got the number:", js_wrapped_printout(6)); } </script> <button onclick="pressBtn2()">Click for number</button>
方案二:临时替换全局Module(适合无法重新编译的场景)
如果你没办法重新编译第二个WASM项目,也可以通过临时替换全局Module对象的方式来隔离两个模块。不过这个方法有一定风险,因为有些Emscripten生成的脚本可能依赖特定的初始化时机,所以优先用方案一哦:
<!-- 第一个模块:正常使用全局Module --> <script src="hello.js"></script> <script> var js_wrapped_fib = Module.cwrap("fib", "number", ["number"]); function pressBtn(){ console.log("The result of fib(5) is:", js_wrapped_fib(5)); } </script> <button onclick="pressBtn()">Click me!</button> <!-- 第二个模块:临时替换全局Module来隔离 --> <script> // 先把原来的全局Module存起来 const originalGlobalModule = window.Module; // 给第二个脚本创建一个新的空Module对象 window.Module = {}; </script> <script src="hello2.js"></script> <script> // 保存第二个模块的实例 const hello2Module = window.Module; // 把全局Module恢复成原来的第一个模块的对象 window.Module = originalGlobalModule; function pressBtn2(){ const js_wrapped_printout = hello2Module.cwrap("printout", "number", ["number"]); console.log("The clicked button got the number:", js_wrapped_printout(6)); } </script> <button onclick="pressBtn2()">Click for number</button>
为什么会出现这个错误?
你看到的Module.arguments被替换成arguments_的报错,本质是第一个脚本初始化时已经修改了全局Module的内部结构,第二个脚本加载时还在按照旧的逻辑去访问Module.arguments,但这个属性已经被Emscripten改成arguments_了——说白了就是两个脚本在抢同一个全局Module对象的控制权,互相打乱了对方的初始化流程。
备注:内容来源于stack exchange,提问作者BenjaminHzo
相关产品推荐
相关产品推荐

