Blog Entry

在React中使用Wasm

在React中使用Wasm

Created
2022/02/07
Updated
2022/02/07

wasm版本

webpack已经有了几个关于WebAssembly的试验特性,支持直接导入wasm文件,这里试验一下看.

直接运行试验项目,或者一步步来

  1. 使用Create React App新建项目:
Terminal window
# 在 xkexp.cpp/wasm 目录
npx create-react-app in-react
cd in-react
yarn
  1. 引入react-app-rewired
Terminal window
yarn add -D react-app-rewired
  1. 建立配置文件config-overrides.js(用于覆盖CRA配置)
/* config-overrides.js */
module.exports = {
webpack: function(config, env) {
config.experiments.asyncWebAssembly = true;
config.experiments.syncWebAssembly = true;
return config;
}
}
  1. 修改脚本
package.json
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test",
+ "test": "react-app-rewired test",
"eject": "react-scripts eject"
}
  1. 运行
Terminal window
yarn
# 调用build-function.ps1脚本,将noprint.c生成xkfunc.wasm
yarn build:wasm
# 启动开发服务
yarn start

mjs版本

运行试验项目

Terminal window
yarn
# 调用build-function-mjs.ps1脚本,将main.c生成xkfunc.mjs
yarn build:mjs
# 启动开发服务
# 启动成功后,控制台应该有调用xkFunc的输出了
yarn start

核心代码

src/App.js

import React, { useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
// 导入mjs
import xkFuncModule from './xkfunc.mjs';
function App() {
// 在useEffect中调用c函数
useEffect(() => {
xkFuncModule()
.then(Module => {
Module.ccall("xkFunc", null, null, null);
})
});
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

目录及文件

└─ root 基础目录
├─ function c目录
│ ├─ main.c c文件
│ └─ noprint.c c文件(无打印)
├─ in-react wasm版本
└─ in-react-mjs mjs版本
└─ scripts 脚本目录
└─ build-function-mjs.ps1 c转mjs脚本

main.c

文件: function/main.c

#include <stdio.h>
int main(int argc, char** argv) {
printf("Hello World!\n");
printf("Hello Function!\n");
return 0;
}
int xkFunc(int argc, char** argv) {
printf("xkFunc be called! return 100\n");
return 100;
}

noprintf.C

文件: function/noprintf.c

#include <stdio.h>
int main(int argc, char** argv) {
return 0;
}
int xkFunc(int argc, char** argv) {
return 100;
}

build-function-mjs.ps1

文件: in-react-mjs/scripts/build-function-mjs.ps1

Terminal window
# 如果没有输出,或者其他诡异错误,试着删除所有中文注释
# 设置emsdk环境变量
&"$Env:EMSDK/emsdk.ps1" construct_env
# 编译
# 输出为src/xkfunc.mjs,没有报错就是成功
Write-Host "emcc compile start"
emcc --no-entry ../function/main.c -o src/xkfunc.mjs `
-s ENVIRONMENT='web' `
-s SINGLE_FILE=1 `
-s EXPORT_NAME='xkFuncModule' `
-s USE_ES6_IMPORT_META=0 `
-s EXPORTED_FUNCTIONS='["_main", "_xkFunc"]' `
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' `
-O3
Write-Host "emcc compile end"

Module not found: Error: Can’t resolve ‘wasi_snapshot_preview1’

emscripten使用了wasi_snapshot_preview1.fd_write用于输出,比如C里面的printf打印的结果,可以通过wasm输出到浏览器的控制台.

前面参考项目中的function/main.c中多次使用printf,导致一直报这个错误.

改用function/noprint.c不用输出,则可以正常在React中编译运行了

参考资料