WebAssembly 是什么?
WebAssembly(wasm)是一种简单的机器模型和具有广泛规范的可执行格式。它被设计成可移植的、紧凑的,并且以或接近本机速度执行。
WebAssembly 有两种表示相同结构的格式:
.wat文本格式(WebAssembly Text)使用 S 表达式;.wasm二进制格式,级别较低,直接供 wasm 虚拟机使用。
安装 Rust 的相关环境
安装 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
安装 wasm-pack
cargo install wasm-pack
创建 Rust 项目
cargo new --lib wasm-react-demo
- 在 Carto.toml 添加 wasm_bindgen 依赖
[dependencies]
+ wasm-bindgen = "0.2"
- 在 src/lib.rs 中添加如下代码
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn big_computation() {
alert("超级耗时的复杂计算逻辑尽量在 rust 实现");
}
#[wasm_bindgen]
pub fn welcome(name: &str) {
alert(&format!("我是 {}!", name));
}
- 编译
wasm-pack build --target web
会提示需要配置
Error: crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:
[lib]
crate-type = ["cdylib", "rlib"]
于是在 Cargo.toml 中配置
+ [lib]
+ crate-type = ["cdylib", "rlib"]
再次执行 wasm-pack build --target web 编译
- 此时在项目根目录下生成 pkg 的文件夹
├── package.json
├── wasm_react_demo_bg.wasm
├── wasm_react_demo_bg.wasm.d.ts
├── wasm_react_demo.d.ts
└── wasm_react_demo.js
创建其React的项目
配置 webpack.config.js 的开发环境
- 安装相关的包
npm i webpack webpack-cli webpack-dev-server html-webpack-plugin clean-webpack-plugin -D
npm i babel-loader @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react -D
npm i @wasm-tool/wasm-pack-plugin -D
npm i react react-dom core-js -S
PS:
i => install
-D => --save-dev
-S => --save
- 配置 webpack.config.js
const { ProgressPlugin } = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
module.exports = {
devtool: "inline-source-map",
entry: "./app/index.jsx",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash:8].bundle.js",
},
devServer: {
compress: true,
port: 8080,
hot: true,
static: "./dist",
historyApiFallback: true,
open: true,
},
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
plugins: [
new ProgressPlugin(),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: `${__dirname}/public/index.html`,
filename: "index.html",
}),
new WasmPackPlugin({
crateDirectory: path.resolve(__dirname, "."),
}),
],
experiments: {
asyncWebAssembly: true,
},
};
配置 babel 编译环境
- 配置 babel.config.js
module.exports = {
presets: [
[
"@babel/preset-env", // 编译基本的es6语法
{
useBuiltIns: "usage", // 按需引入兼容的实现的es6的一些浏览器不支持的方法,这样就不需要在入口文件引入 import "babel-polyfill"; 造成文件体积增大
corejs: 3, // 配合使用一些不兼容的 es6 的函数实现, 3 版本已经不需要依赖 babel-polyfill
targets: { // 这个配置放到 package.json 中配置了 "browserslist":{}
chrome: "58",
ie: "11"
}
}
],
"@babel/preset-react"
],
plugins: [
"@babel/plugin-transform-runtime" // 编译 es7 的一些高级语法编译, 比如 async/await
]
}
- 配置 package.json 的执行脚本
"scripts": {
"dev": "webpack serve --mode=development",
"build": "webpack --mode=production",
"build:wasm": "cargo build --target wasm32-unknown-unknown",
"build:bindgen": "wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_react.wasm --out-dir build",
"build:all": "npm run build:wasm && npm run build:bindgen && webpack --mode=production"
},
- React 中调用 WebAssembly 中的函数
import React, { useState } from "react";
const App = () => {
const rustApp = import("../pkg");
const [name, setName] = useState("");
const handleChange = (e) => {
setName(e.target.value);
};
const big_computation = async () => {
const r = await rustApp;
r.big_computation();
};
const handleClick = async () => {
const r = await rustApp;
r.welcome(name);
};
return (
<>
<div>
<h1>Hello React Rust WebAssembly</h1>
<button onClick={big_computation}>Run Computation</button>
</div>
<div>
<input type="text" onChange={handleChange} />
<button onClick={handleClick}>Click me</button>
</div>
</>
);
};
export default App;