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