react+rust+webAssembly(wasm)示例
前言:WebAssembly(简称wasm)已经出来有几年了,在一些需要高性能的web应用场景中,wasm技术可以让代码执行效率大大提升。react做为目前大厂主流的前端框架之一,搭配上最近几年一直越来越火的Rust语言,可以很好的结合起来,形成wasm的解决方案。国外有高人给出了一篇详细的英文入门教程(见本文最后的参考文章链接),下面是主要使用步骤。
一、准备工作及环境
本文示例环境为:mac环境(12.3.1 Monterey) + nodejs(v 18.5.0) + react (v 18.2) + rustc(v 1.62.0) + cargo(1.57.0) + wasm-bindgen(v 0.2.82) + wasm-pack(v 0.10.3)
请大家先在本机安装好node环境(这是必须的,包括npm包管理工具),另外为了提高一些依赖包的下载速度,建议设置npm的资源为国内淘宝镜像,另外因为要使用rust做为后端语言,所以rust/cargo环境也得有
二、用React脚手架创建项目模板
2.1 先建一个基础目录,比如 wasm_project,进入该目录下
mkdir wasm_project cd wasm_project
2.2 创建react项目模板
npx create-react-app react-wasm-tutorial --template typescript
等待一段时间的下载后,就会创建一个react-wasm-turorial的项目模板,用vscode打开它,可以看看目录结构
就是最常见的react项目结构,其中App.tsx是入口组件,代码如下:
import React from 'react'; import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</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;
终端窗口下,用npm start把项目跑起来,大致是下面这个样子:
三、创建rust wasm项目
仍然保持在wasm_project/react-wasm-tutorial目录下,终端输入命令:
cargo new wasm-lib --lib
将创建1个rust的lib项目,目录结构如下:
lib.rs中的示例代码没啥用,干掉它,重新写个add加法函数:
// lib.rs pub fn add(a: i32, b: i32) -> i32 { a + b } #[test] fn add_test() { assert_eq!(1 + 1, add(1, 1)); }
终端下进入wasm-lib目录后,可以用cargo test测试下代码,正常的话,会看到类似下面的图,表示单元测试通过
到目前为止,所有代码跟wasm半毛钱关系没有,别急!马上就来了:
参考下图,修改Cargo.toml文件,核心就是添加wasm-bindgen依赖,并告诉rust编译器,要生成一个符合C语言规范的动态链接库(C Dynamic Lib)
然后微调lib.rs,参考下图,主要是将add函数标记为允许在wasm环境中调用
依然保持在wasm-lib目录下,安装wasm-pack
cargo install wasm-pack
将rust代码编译成wasm专用的二进制文件,需要这个工具。
停一下,思考1个问题:目前react项目与rust项目,虽然都放在1个根目录下了,但是它俩好象没啥关系? react项目运行时,咋知道要使用rust编译出来的东西呢?
修改package.json,参考下图,加一行:
"build:wasm": "cd wasm-lib && wasm-pack build --target web --out-dir pkg",
这样,我们就能用来编译rust代码来生成wasm
npm run build:wasm
注:上面的命令,必须在wasm_project/react-wasm-tutorial目录下执行哈。
编译完后,可以看到多出了pkg目录,如下图:
查看wasm_lib.d.ts源码,可以发现add函数已经被export导出了。继续思考一下:pkg目录下生成的东西,react项目在运行时,怎么就知道要加载它呢?大家知道,前端一些依赖的模块,都是放在node_modules下的,所以得将pkg的目录安装到node_modules下
npm install ./wasm-lib/pkg
安装完成后,node_modules目录下就有wasm_lib里的东西了(如下图,基本上就是直接复制过来而已)
可以看到,生成的.wasm文件其实非常小,只有168B。这里再提1个细节,经过刚才这一番折腾后,打开package.json
会发现依赖项里,多出一项"wasm-lib": "file:wasm-lib/pkg"
四、在react中调用wasm
参考下图,修改App.tsx
重新npm run start
可以看到rust里的add,已经在react中调用成功
五、性能对比
原文中这个add太过于简单,体现不出wasm的性能优势,我们来加一个经典的"斐波那契数列"示例
然后参考前面的步骤,重新编译&安装
npm run build:wasm npm install ./wasm-lib/pkg
然后回到App组件中,调用这个新方法
重新跑一下,观察console控制台的输出:
chrome上的对比效果
firefox上的对比效果:
貌似firefox上wasm的性能提升效果更明显。
六、可能会遇到的坑
6.1、rust中的function,必须定义成pub类型,否则编译时就报错了
6.2、react项目,可以用npm intall xxx 或yarn add xxx来安装包,但是二者别混用(即:一会儿用npm install,一会儿yarn add ),这样node_modules目录,很容易混乱冲突,造成项目启用不了,如果真遇到这种情况
npm install -g rimraf rimraf node_modules rimraf package-lock.json npm cache clear --force npm config set registry https://registry.npm.taobao.org npm install
可以在项目根目录下,终端窗口尝试上述命令,重新安装。
附文中示例代码:https://github.com/yjmyzz/react-rust-wasm-demo
参考文章:
https://tkat0.github.io/posts/how-to-create-a-react-app-with-rust-and-wasm
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。