1、环境介绍:
rust 1.61.0
cargo 1.61.0
wasm-pack 0.10.3
node 18.10.0
yarn 1.22.10
2、创建react项目
//本项目名为wasmapp-react
//1、创建react项目
npx create-react-app wasmapp-react --template typescript
3、创建rust项目
//2、创建rust项目 rust项目名为wasm-hello
cd wasmapp-react
cargo new --lib wasm-hello
当前项目目录结构如下:
(base) ➜ wasmapp-react (master) ✗ tree
.
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ └── setupTests.ts
├── tsconfig.json
└── wasm-hello
├── Cargo.toml
└── src
└── lib.rs
4、安装react依赖
//以下按顺序执行
yarn
yarn add @terra-money/terra.js
yarn add @terra-money/wallet-provider
将terra依赖引入index.tsx,并修改index.tsx内容:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import 'semantic-ui-css'
import {getChainOptions ,WalletProvider} from '@terra-money/wallet-provider'
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
getChainOptions().then(chainOptions => {
root.render(
<WalletProvider {...chainOptions}>
<App />
</WalletProvider>
);
})
reportWebVitals();
启动项目:
yarn start
上述步骤执行完,浏览器应该会报如下错误:
1、Uncaught ReferenceError: Buffer is not defined
at ./node_modules/@terra-money/terra.js/dist/core/PublicKey.js (bundle.js:28070:34)2、Uncaught ReferenceError: stream is not defined
发生报错后,页面是无法正常显示,我们需要做如下工作:
5、报错更改
出现上述错误是因为浏览器加载不到Buffer,我们是否可以通过yarn add buffer解决呢,并不行。
为解决上述错误,我们需要对webpack配置进行改造:
首先我们需要新增一些react依赖包:
yarn add --dev react-app-rewired process crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer next-tick
安装完依赖之后我们需要修改两个文件:
(1)package.json
in package.json
//before edit
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
// after edit
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"build:wasm": "cd wasm-hello && wasm-pack build --target web --out-dir pkg",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
(2)tsconfig.json
{
"compilerOptions": {
"paths" : {
"crypto": ["./node_modules/crypto-browserify"],
"stream": ["./node_modules/stream-browserify"],
"assert": ["./node_modules/assert"],
"http": ["./node_modules/stream-http"],
"https": ["./node_modules/https-browserify"],
"os": ["./node_modules/os-browserify"]
},
"target": "ES2018",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"watch": true,
},
"include": [
"src"
]
}
做完上述修改工作我们还需新建两个文件
(1)项目根目录创建config-overrides.js
此文件为了替换webpack的部分配置
const webpack = require('webpack');
module.exports = function override(config) {
const fallback = config.resolve.fallback || {};
Object.assign(fallback, {
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"assert": require.resolve("assert"),
"http": require.resolve("stream-http"),
"https": require.resolve("https-browserify"),
"os": require.resolve("os-browserify"),
"url": require.resolve("url")
})
config.resolve.fallback = fallback;
config.plugins = (config.plugins || []).concat([
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
})
])
config.ignoreWarnings = [/Failed to parse source map/];
return config;
}
(2) 在src目录下新建polyfills.ts
import { Buffer } from 'buffer';
(window as any).global = window;
global.Buffer = Buffer;
global.process = {
env: { DEBUG: undefined },
version: '',
nextTick: require('next-tick')
} as any;
(3)在index.tsx引入polyfills.ts:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import './polyfills';
import reportWebVitals from './reportWebVitals';
import {getChainOptions ,WalletProvider} from '@terra-money/wallet-provider'
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
getChainOptions().then(chainOptions => {
root.render(
<WalletProvider {...chainOptions}>
<App />
</WalletProvider>
);
})
reportWebVitals();
6、钱包连接 (至此可以参照terra官方文档调用钱包)
7、wasm调用
rust编写的函数要让react访问到需要使用rust的wasm-bindgen依赖
只需执行cargo install wasm-pack即可,wasm-pack在运行时会自动下载wasm-bindgen
我们需要修改Cargo.toml文件如下:
[package]
name = "wasm-hello"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2.63"
在lib.rs文件中定义我们的函数:
use wasm_bindgen::prelude::*;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(hel:&str) {
let init = "Hello, wasm-vue".to_string();
let res =init + &hel;
alert(&res);
}
至此我们需将rust编译为wasm形式:
在步骤5中,我们已经在package.json中配置了build:wasm命令,以编译rust
所以,我们只需执行:
yarn build:wasm
执行完之后, wasm-hello文件夹中会新增pkg文件夹
最后,我们需要将rust编译后的内容install到node_modules中:
// 项目根目录执行
yarn add ./wasm-hello/pkg
至此,我们的钱包调用和wasm集成都完成了,重新启动项目即可
项目代码见github:
GitHub - shaozhupeng/wasmapp-react: react+typescript+rust + wasm, terra wallet provider, terra.js