WebAssembly导出的函数怎么在JS里调用不生效?
我用Rust编译了一个.wasm文件,导出了一个add函数,但在JS里调用时总是undefined,不知道是哪步出错了。已经确认wasm加载成功了,但instance.exports里看不到我的函数。
这是我在HTML里加载和调用的代码:
<script type="module">
const wasm = await WebAssembly.instantiateStreaming(fetch('pkg/my_wasm_bg.wasm'));
console.log(wasm.instance.exports); // 这里没看到add函数
// const result = wasm.instance.exports.add(1, 2); // 报错:add is not a function
</script>
首先检查你的Rust代码里有没有加 #[no_mangle] 和 #[export_name = "add"] 这两个属性,像这样:
其次,注意你在JS里加载的是原始wasm文件,但Rust编译时会生成两个文件:一个是纯wasm,另一个是包含胶水代码的js文件。建议用pkg/my_wasm_bg.js里的默认导出:
我之前就是因为直接用wasm文件搞了半天都不行,后来才发现要通过胶水代码来初始化。这东西真挺折腾人的,但弄明白原理就好办了。
问题原因:wasm-bindgen在编译时会生成两部分东西,一个是.wasm二进制文件,另一个是.js胶水代码。你只加载了.wasm,但那些导出函数实际上是通过JS胶水代码包装之后才能正常调用的。
正确的做法是这样的:
首先,确保你的Rust代码里函数标记了 #[wasm_bindgen],比如:
然后编译的时候用 wasm-pack build --target web
在HTML/JS里正确的调用方式应该是:
你之前那么写的问题是,wasm-bindgen导出的函数实际上是通过JS胶水代码里的WebAssembly.Instance在内部处理过的,不是直接放在instance.exports里的。你需要先跑一遍init(),它会帮你正确实例化wasm并把导出函数暴露出来。
如果你不想用wasm-bindgen,想直接裸写wasm导出,那也行,但就得用wasm-bindgen-cli或者直接用wat手写WebAssembly文本格式了,那样导出函数会直接出现在instance.exports里。不过既然你已经在用Rust了,用wasm-bindgen还是最省心的办法。