WebAssembly导出的函数怎么在JS里调用不生效?

欧阳可慧 阅读 67

我用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>
我来解答 赞 7 收藏
二维码
手机扫码查看
2 条解答
Tr° 胜换
这个问题我也踩过坑,血泪教训啊。你这种情况通常是导出函数名被混淆或者没有正确配置导致的。

首先检查你的Rust代码里有没有加 #[no_mangle] 和 #[export_name = "add"] 这两个属性,像这样:

#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}


其次,注意你在JS里加载的是原始wasm文件,但Rust编译时会生成两个文件:一个是纯wasm,另一个是包含胶水代码的js文件。建议用pkg/my_wasm_bg.js里的默认导出:

import init, { add } from './pkg/my_wasm_bg.js';

async function run() {
await init();
console.log(add(1, 2)); // 现在应该能正常调用了
}

run();


我之前就是因为直接用wasm文件搞了半天都不行,后来才发现要通过胶水代码来初始化。这东西真挺折腾人的,但弄明白原理就好办了。
点赞
2026-03-29 21:13
开发者星宇
这个问题很典型,你应该是用了wasm-bindgen来编译Rust代码对吧?wasm-bindgen生成的wasm文件不能直接手动实例化,它需要配合生成的JS胶水代码一起使用。

问题原因:wasm-bindgen在编译时会生成两部分东西,一个是.wasm二进制文件,另一个是.js胶水代码。你只加载了.wasm,但那些导出函数实际上是通过JS胶水代码包装之后才能正常调用的。

正确的做法是这样的:

首先,确保你的Rust代码里函数标记了 #[wasm_bindgen],比如:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}


然后编译的时候用 wasm-pack build --target web

在HTML/JS里正确的调用方式应该是:

// 导入wasm-bindgen生成的JS初始化函数
import init, { add } from './pkg/my_wasm.js';

async function run() {
// 先调用init()初始化wasm模块
await init();

// 然后直接调用导出的函数
const result = add(1, 2);
console.log(result); // 输出3
}

run();


你之前那么写的问题是,wasm-bindgen导出的函数实际上是通过JS胶水代码里的WebAssembly.Instance在内部处理过的,不是直接放在instance.exports里的。你需要先跑一遍init(),它会帮你正确实例化wasm并把导出函数暴露出来。

如果你不想用wasm-bindgen,想直接裸写wasm导出,那也行,但就得用wasm-bindgen-cli或者直接用wat手写WebAssembly文本格式了,那样导出函数会直接出现在instance.exports里。不过既然你已经在用Rust了,用wasm-bindgen还是最省心的办法。
点赞 1
2026-03-11 14:01