在 Next JS 中使用使用 Emscripten 编译的 WebAssembly 模块

Use WebAssembly module compiled with Emscripten in Next JS

我正在尝试使用使用 Emscripten 编译的导入 WebAssembly 模块构建 Next JS 项目。 这个问题似乎与 WebPack 加载器无法通过生成的 .js 导入 .wasm 模块有关。 非常感谢您的帮助。

我的 C++ 文件 hello.cpp:

#include <math.h>

extern "C" {

int int_sqrt(int x) {
  return sqrt(x);
}

}

我编译使用:

em++ hello.cpp -o "../v2/lib/wasm/test.js" -s MODULARIZE -s WASM=1 -s EXPORT_NAME="SZU" -s ENVIRONMENT="web" -s EXPORTED_FUNCTIONS=_int_sqrt -s EXPORTED_RUNTIME_METHODS=ccall,cwrap

我尝试在组件之一中使用该模块,如下所示:

import SZU from "../../../../../../wasm/test";
var int_sqrt = SZU().cwrap("int_sqrt", 'number', ['number'])

当我 运行 npx next build 构建失败时:

Error: not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)

我希望能够使用 npx next build 构建项目,然后使用 npx next start 启动服务器,并能够与 .wasm/[=15= 中导出的函数进行交互] 由 Emscripten 生成的模块文件。

wasm 的情况是它将编译成 2 个文件:一个 js 文件和一个 wasm 文件。 wasm 文件无法捆绑到您的应用程序中。您必须将其作为静态资产提供(因为它基本上是二进制代码),并配置您的路径以与您所需的导入保持一致。

您可能还想先确保它能正常工作。尝试编写一个导入它的 nodejs 脚本并对其进行测试 运行。或者你可以尝试将它导入 nextjs API 路由,并通过从 client-side 获取来调用它,看看你编译的 wasm 是否工作。

@lab的帮助下,我意识到我的方法是错误的。这是我让我的 WASM 模块工作的方式:

  1. 这是我的 hello.cpp 文件。重要注意 extern "C" {} 块:
#include <math.h>

extern "C" {

int int_sqrt(int x) {
  return sqrt(x);
}

}
  1. 我用em++编译,目标是下一个public文件夹。我们需要像 @lab 指出的那样静态地提供 .wasm.js
em++ hello.cpp -o "../v2/public/wasm/test.js" -s MODULARIZE -s WASM=1 -s EXPORT_NAME="SZU" -s ENVIRONMENT="web" -s EXPORTED_FUNCTIONS=_int_sqrt -s EXPORTED_RUNTIME_METHODS=ccall,cwrap
  1. 我使用 next/script 标签加载 Emscripten 生成的 js 文件,如下所示:
<Script src="/wasm/test.js" strategy='beforeInteractive'/>
  1. 在我想使用我的 WASM 模块的组件中,我将它加载到 next/useEffect 钩子中,用 Emscripten cwrap 包装它并使用 next/useRef 将函数存储在引用对象中像这样:
useEffect(() => {
    if(typeof (window as any).SZU === 'function' && WASM.current === null){
        console.log("loading WASM!");
        (window as any).SZU()
            .then((wasm: WASM) => {
                console.log("got WASM!");
                WASM.current = wasm;
                int_sqrt.current = WASM.current.cwrap("int_sqrt", "number", ["number"]);
            });
    }
}, []);
  1. 稍后我可以像这样调用函数:
console.log(`Square root of 16 is ${int_sqrt.current(16)}`);

我希望这对遇到同样问题的人有所帮助。