前端Rust二进制/wasm全平台构建流程简述
前言
开门见山,现代前端 Rust 构建基本分三大类,即 构建 .wasm 、构建 .node 二进制 、构建 swc 插件。
入门详见 《 前端Rust开发WebAssembly与Swc插件快速入门 》 。
对于单独开发某一类的流程,在上述参考文章中已有介绍,但对于一次开发后全平台构建发布,上述文章并未涉猎,基于此,本文将快速介绍一个最简的 全平台构建 (包括二进制 .node 与 .wasm ) 前端 Rust 包的开发流程是怎样的。
注:我们默认读者已掌握构建三大类前端 Rust 包的知识。
正文
Rust workspace
以 workspace 组织代码仓库,核心逻辑全部独立为一个子包,参考如下:
- crates- binding_node # 基于 napi 分发 `.node` 二进制- binding_wasm # 基于 wasm-pack 分发 `.wasm`- core # 核心逻辑- ... # 其他解耦
binding_node
其中 binding_node 为 node 构建出口,引用核心逻辑后暴露 API ,简式参考如下:
// binding_node/src/lib.rs#[macro_use]
extern crate napi_derive;use napi::{bindgen_prelude::AsyncTask, Env, Task};
use core::{core_process, IInput, IResult};// ⬇️ 同步部分
#[napi]
pub fn method_sync(input: IInput) -> Result<IResult, anyhow::Error> {core_process(input)
}// ⬇️ 异步部分
pub struct TaskExecutor {input: IInput,
}pub struct ProcessTask {task: TaskExecutor,
}impl Task for ProcessTask {type Output = IResult;type JsValue = IResult;fn compute(&mut self) -> napi::Result<Self::Output> {self.task.process().map_err(|err| napi::Error::from_reason(&err.to_string()))}fn resolve(&mut self, _env: Env, output: Self::Output) -> napi::Result<Self::JsValue> {Ok(output)}
}impl TaskExecutor {pub fn process(&self) -> Result<IResult, anyhow::Error> {core_process(self.input.clone())}
}#[napi(ts_return_type="Promise<IResult>")]
pub fn method(input: IInput) -> AsyncTask<ProcessTask> {AsyncTask::new(ProcessTask {task: TaskExecutor { input },})
}
注:此处为最简函数式示例,涉及 异步信号、错误处理、错误打印、导出多方法的复杂类 等情况时请自行处理。
通常情况提供最普通的 同步函数 方法已足够。
类型处理
为了尽可能减少工作量,让 napi 自动生成 .d.ts 类型,需给结构对象加上 #[napi] 宏才能生成类型,但所有结构声明在 crates/core 核心逻辑包中,而我们的 napi 出口在 crates/binding_node 。
一种解法是条件编译,提供 feature = "node" 的特定模式,参考如下:
# core/Cargo.toml[features]
default = []
node = ["napi", "napi-derive"][dependencies]
napi = { ..., optional = true }
napi-derive = { ..., optional = true }
// 条件编译宏#[macro_export]
#[cfg(feature = "node")]
macro_rules! multi_env {($($items:item)*) => {use napi_derive::napi;$(#[napi(object)]$items)*};
}
使用参考:
multi_env! {pub struct IInput {pub input: ...,}pub struct IResult {pub output: ...,}// ...
}
综上,通过特定 node feature 方式,在引用时条件添加 #[napi] 来做到自动生成类型。
人工应对复杂类型
如 想自行管理导出方法类型的暴露情况、涉及 class 等复杂的类型 、无法自动识别生成 等情况,可以人工编写整份 .d.ts 文件,但十分耗费精力。
对于 异步情况等 引发的动态值转换,而无法自动识别类型的,可尝试 napi 默认自带的类型选项,如 #[napi(ts_return_type="...")] 等选项来辅助修改生成的类型,省时省力。
构建
对于多平台,我们通常需要依赖 GitHub Actions 来进行多平台构建,在 CI 中构建、测试后发布到 npm 。
现代常用构建对象 napi 列表如下:
"triples": {"defaults": false,"additional": ["x86_64-apple-darwin","aarch64-apple-darwin","x86_64-pc-windows-msvc","aarch64-pc-windows-msvc","x86_64-unknown-linux-gnu","aarch64-unknown-linux-gnu","x86_64-unknown-linux-musl","aarch64-unknown-linux-musl"]},
最常用的即如上 8 个平台,酌情构建 armv7-unknown-linux-gnueabihf ,必要时采用 wasm 兜底即可。
此部分过于冗长且模板化,可参考 swc 等项目的构建 CI 来取用。
binding_wasm
其中 binding_wasm 为 wasm 构建出口,引用核心逻辑后暴露 API ,简式参考如下:
use wasm_bindgen::prelude::*;use core::{core_process, IInput, IResult};#[wasm_bindgen(js_name = "methodSync")]
pub fn method_sync(input: IInput) -> Result<IResult, JsError> {core_process(input).map_err(|err| JsError::new(&err.to_string()))
}#[wasm_bindgen(typescript_custom_section)]
const INTERFACE_DEFINITIONS: &'static str = r#"
export function method(config: IInput): Promise<IResult>;
"#;#[wasm_bindgen(skip_typescript)]
pub fn method(input: IInput) -> js_sys::Promise {wasm_bindgen_futures::future_to_promise(async {core_process(input).map(|r| serde_wasm_bindgen::to_value(&r).unwrap()).map_err(|err| JsValue::from_str(&err.to_string()))})
}
注:此处为最简函数式示例,涉及 异步、错误处理、错误打印 等情况时请自行修订处理。
通过 serde-wasm-bindgen 来动态转换 JS 值与 Rust 中的结构。
通常情况提供最普通的 同步函数 方法已足够。
类型处理
为了尽可能减少工作量,我们使用 tsify 做自动类型生成,同上文中相同,采用条件编译,提供 feature = "wasm" 模式:
# core/Cargo.toml[features]
default = []
wasm = ["tsify", "wasm-bindgen"][dependencies]
tsify = { ..., optional = true }
wasm-bindgen = { ..., optional = true}
// 条件编译宏#[macro_export]
#[cfg(feature = "wasm")]
macro_rules! multi_env {($($items:item)*) => {use tsify::Tsify;use serde::{Deserialize, Serialize};use wasm_bindgen::prelude::*;$(#[derive(Tsify, Serialize, Deserialize)]#[tsify(into_wasm_abi, from_wasm_abi)]$items)*};
}// 兜底用
#[macro_export]
#[cfg(all(not(feature = "wasm"), not(feature = "node"),))]
macro_rules! multi_env {($($tokens:tt)*) => {$($tokens)*};
}
人工应对复杂类型
如 想自行管理导出方法类型的暴露情况、无法自动识别生成 等情况,可以人工编写整份 .d.ts 文件。
对于 异步情况等 引发的动态值转换,而无法自动识别类型的,尝试 #[wasm_bindgen(skip_typescript)] 跳过自动生成类型后,使用 #[wasm_bindgen(typescript_custom_section)] 人工插入少量类型声明来解决,节省编写时间。
构建
由于 wasm 无需依赖本机环境,根据情况可选在云构建或本地构建均可,主要包含 web 用途与 nodejs 用途的 wasm 产物构建。
web 用途
web 用途的 wasm 产物主要用于网页应用,playground 等,构建命令参考:
# web 用途cd crates/binding_wasm && wasm-pack build --verbose --out-dir ./output/wasm_web --out-name index --release
web 用途的构建产物包含 ESM 格式胶水代码 ,可直接将产物整体用在 webpack 项目导入,对于 webpack 5 可直接开启 async webassembly 特性直接适配项目:
// webpack.config.jsexperiments: {asyncWebAssembly: true,},
import * as wasm from '/path/to/wasm-output'
// 或使用异步 await import() 延时、按需加载
nodejs 用途
nodejs 用途主要用于非主流平台兜底,如 边缘函数、serverless 等环境,构建命令参考:
# nodejs 用途cd crates/binding_wasm && wasm-pack build --target nodejs --verbose --out-dir ./output/wasm --out-name index --release
和 web 用途构建命令区别在于特定了 --target nodejs ,这会得到 CJS 格式产物代码,可直接用于 nodejs 。
使用与安装时机
对于两种 wasm 包,通常命名为 @scope/wasm ( nodejs 用途) 、@scope/wasm-web ( web 用途),在对应平台下,可直接安装该包来使用。
同时,对非主流架构环境,我们一般在主包的 postinstall 时进行脚本检测,并在需要时自动安装 @scope/wasm 包来兜底,具体逻辑较冗长且模板化,可参考 swc 等项目取用即可。
如何安装指定平台包
最新版本的 pnpm v8 支持配置 pnpm.supportedArchitectures 来安装想要的平台包,即使你不在某个平台上,这通常用于 wasm 安装校验:
# .npmrc
# 关闭 postinstall 缓存
side-effects-cache=false
# 打印 postinstall 日志
reporter=append-only
// package.json// ↓ 该配置将匹配不到任何 `.node` 的平台包,于是自动 fallback 到 wasm 兜底包"pnpm": {"supportedArchitectures": {"os": ["unknown"],"cpu": ["x64"]}}
wasm 产物优化
通常 .node 二进制产物由 Rust 生产构建后自动优化,加上 strip 优化体积已足够。
对于 wasm 一般采用 wasm-opt 优化体积,默认 wasm-pack 生产构建最终阶段会自动下载相关工具并执行优化,如遇网络问题,可关闭自动优化,转为手动下载后执行优化:
# 提前下载好执行工具,防止网络问题cargo install wasm-bindgen-cli
# binding_wasm/Cargo.toml# 关闭 wasm-opt 自动优化,之后手动优化
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
# 下载 wasm-opt 工具并解压# 最新版本见:https://github.com/WebAssembly/binaryen/releasescurl -L https://github.com/WebAssembly/binaryen/releases/download/version_116/binaryen-version_116-x86_64-macos.tar.gz -o ./binaryen.tar.gzmkdir ./.cachetar -xvf ./binaryen.tar.gz -C ./.cache# 优化 wasm 产物./.cache/binaryen-version_116/bin/wasm-opt -Oz -o ./output/wasm/index_bg.wasm ./output/wasm/index_bg.wasm
总结
在全平台构建时,编写 十分大量 的人力脚本与文件操作在所难免,如有可能,可将其统一抽象化,方便下次取用。
由于流程更偏向于固定模板化,在实践时,请自行参考相关项目自取所需即可。
相关文章:
前端Rust二进制/wasm全平台构建流程简述
前言 开门见山,现代前端 Rust 构建基本分三大类,即 构建 .wasm 、构建 .node 二进制 、构建 swc 插件。 入门详见 《 前端Rust开发WebAssembly与Swc插件快速入门 》 。 对于单独开发某一类的流程,在上述参考文章中已有介绍,但对于…...
加解密算法相关技术详解
文章目录 简介工作机制加解密对称密钥算法非对称密钥算法 数字信封数字签名数字证书技术对比 推荐阅读 简介 随着网络技术的飞速发展,网络安全问题日益重要,加解密技术是网络安全技术中的核心技术,是最常用的安全保密手段。 加密࿱…...
Clickhouse学习笔记(13)—— Materialize MySQL引擎
该引擎用于监听 binlog 事件,类似于canal、Maxwell等组件 ClickHouse 20.8.2.3 版本新增加了 MaterializeMySQL 的 database 引擎,该 database 能映射到 MySQL中的某个database ,并自动在ClickHouse中创建对应ReplacingMergeTree。 ClickHous…...
《QT从基础到进阶·二十四》按钮组QButtonGroup,单选框QRadioButton和多选框QCheckBox
1、按钮组QButtonGroup 如果有多个单选按钮,可以统一放进一个按钮组。 图中有三个单选按钮放进了一个QGroupBox,并且设置了水平布局,现在要将这三个单选按钮放进一个按钮组,之前的想法是先把三个按钮加入按钮组,再把按钮组放进QG…...
Ansible--playbook剧本
目录 一、playbook: playbook的组成: palybook的编写注意事项 二、playbook的编写格式(示例) 2.1 编写yaml文件 2.2 定义、引用变量 2.3 指定远程主机sudo切换用户 2.4 when条件判断 2.5 迭代 2.6 Templates 模块 2.7 tags 模…...
MacOS下VMware Fusion配置静态IP
前言 在虚拟机安装系统后,默认是通过DHCP动态分配的IP,这会导致每次重启虚拟机ip都可能会改变,使用起来会有很多不便。 配置静态IP 查看主机网关地址 cat /Library/Preferences/VMware\ Fusion/vmnet8/nat.conf 查看主机DNS,m…...
三、机器学习基础知识:Python常用机器学习库(中文文本分析相关库)
文章目录 1、Jieba库1.1 主要函数1.2 词性标注1.3 关键词提取 2、WordCloud库2.1 常见参数2.2 词云绘制 文本分析是指对文本的表示及其特征的提取,它把从文本中提取出来的特征词进行量化来表示文本信息,经常被应用到文本挖掘以及信息检索的过程当中。 1、…...
Nginx 使用笔记大全(唯一入口)
Linux服务器因为Nginx日志access.log文件过大项目无法访问 项目处于运行状态下无法访问,第一步查看磁盘状态 1、查看磁盘状态 df -h 2、查找100M以上的文件 find / -size 100M |xargs ls -lh 3、删除文件 rm -rf /usr/local/nginx/logs/access.log 4、配置nginx.…...
数据结构-二叉排序树(建立、查找、修改)
二叉排序树概念 二叉排序树是动态查找表的一种,也是常用的表示方法。 其中,它具有如下性质: 1.若它的左子树非空,则其左子树的所有节点的关键值都小于根节点的关键值。 2.若它的右子树非空,则其右子树的所有节点的…...
Linux 性能优化之使用 Tuned 配置优化方案
写在前面 考试整理相关笔记博文内容涉及 Linux tuned 调优工具的简单认知调优配置文件的简单说明,自定义调优方案介绍理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意ÿ…...
Day02_《MySQL索引与性能优化》
文章目录 一、SQL执行顺序二、索引简介1、关于索引2、索引的类型Btree 索引Btree 索引 三、Explain简介四、Explain 详解1、id2、select_type3、table4、type5、possible_keys6、key7、key_len8、ref9、rows10、Extra11、小案例 五、索引优化1、单表索引优化2、两表索引优化3、…...
(只需三步)Vmvare tools安装教程,实现与windows互通复制粘贴与文件拖拽
首先确保Ubuntu是联网的,如果连不上网可以参考我的这个联网教程,也很简单 (只需三步)虚拟机上vm的ubuntu不能联上网怎么办-CSDN博客 第一步:卸载之前的tools,确保没有残留 sudo apt-get autoremove open-vm-tools 第…...
Android自定义控件:一款多特效的智能loadingView
先上效果图(如果感兴趣请看后面讲解): 1、登录效果展示 2、关注效果展示 1、【画圆角矩形】 画图首先是onDraw方法(我会把圆代码写上,一步一步剖析): 首先在view中定义个属性:priv…...
C语言之初阶指针
一、指针: 其实按照我的理解,当我们写c语言程序的时候,创建的变量,数组等都要在内存上开辟空间。而每一个内存都有一个唯一的编号,这个编号也被称为地址编号,就相当于,编号地址指针。 二、指针…...
MongoDB基础知识~
引入MongoDB: 在面对高并发,高效率存储和访问,高扩展性和高可用性等的需求下,我们之前所学习过的关系型数据库(MySql,sql server…)显得有点力不从心,而这些需求在我们的生活中也是随处可见的,例如在社交中…...
41. 缺失的第一个正数
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1: 输入:nums [1,2,0] 输出:3示例 2: 输入:nums [3…...
数据结构—数组栈的实现
前言:各位小伙伴们我们前面已经学习了带头双向循环链表,数据结构中还有一些特殊的线性表,如栈和队列,那么我们今天就来实现数组栈。 目录: 一、 栈的概念 二、 栈的实现 三、 代码测试 栈的概念: 栈的概念…...
AI大模型低成本快速定制秘诀:RAG和向量数据库
文章目录 1. 前言2. RAG和向量数据库3. 论坛日程4. 购票方式 1. 前言 当今人工智能领域,最受关注的毋庸置疑是大模型。然而,高昂的训练成本、漫长的训练时间等都成为了制约大多数企业入局大模型的关键瓶颈。 这种背景下,向量数据库凭借其独特…...
Please No More Sigma(构造矩阵)
Please No More Sigma 给f(n)定义如下: f(n)1 n1,2; f(n)f(n-1)f(n-2) n>2; 给定n,求下式模1e97后的值 Input 第一行一个数字T,表示样例数 以下有T行,每行一个数,表示n。 保证T<100,n<100000…...
HTML设置标签栏的图标
添加此图标最简单的方法无需修改内容,只需按以下步骤操作即可: 1.准备一个 ico 格式的图标 2.将该图标命名为 favicon.ico 3.将图标文件置于index.html同级目录即可 为什么我的没有变化? 答曰:ShiftF5强制刷新一下网页就行了...
别再乱用分支了!Flowable四种网关(排他/并行/包容/事件)实战选型指南
Flowable四大网关实战选型:从混乱到精准的决策艺术当你在设计一个请假审批流程时,是否遇到过这样的困惑:部门经理审批后需要同时通知HR和财务,但某些特殊情况下又需要跳过财务直接归档?这种看似简单的业务需求…...
Android 11开发避坑:为什么你的App获取的Wifi MAC地址总是变?手把手教你配置固定MAC
Android 11开发实战:彻底解决Wifi MAC地址随机化问题最近在开发一个设备管理系统时,遇到了一个棘手的问题:我们的App在Android 11设备上获取的Wifi MAC地址每次都不一样,导致基于MAC地址的设备识别功能完全失效。经过一周的深入研…...
2026在线测评系统十大量表对比:信效度与场景全解析
【30s 核心摘要】2026 年在线测评成人才管理刚需,信效度与场景适配成选型核心。本文聚焦十大量表,从信度、效度、适配场景等维度深度对比,重点解析问卷星、北森、金数据等主流平台的量表能力与落地效果,为企业、高校及机构提供科学…...
ARM指令追踪技术及TRCVICTLR寄存器详解
1. ARM指令追踪技术概述在嵌入式系统开发和调试过程中,指令追踪(Instruction Trace)是一项至关重要的技术。它通过硬件机制记录处理器的执行流程,为开发者提供程序运行的完整轨迹。ARM架构从v7开始引入嵌入式跟踪宏单元࿰…...
App Inventor蓝牙调试避坑指南:从连接失败到数据乱码,一次讲清所有常见问题
App Inventor蓝牙调试避坑指南:从连接失败到数据乱码的实战解决方案在移动应用开发领域,蓝牙通信一直是实现设备间短距离数据交换的核心技术之一。对于使用App Inventor的开发者而言,蓝牙模块提供了无需复杂编码即可实现无线通信的便捷途径。…...
论文创新点像挤牙膏?导师强推这几个AI论文平台
想写论文又快又好,关键是用对 AI 工具、走对流程——资深教授普遍推荐:千笔AI(中文全流程首选) 豆包学术版(轻量高效) DeepSeek 学术版(理工 / 长文本) Grammarly Academicÿ…...
为内部知识库问答机器人接入Taotoken多模型增强回答效果
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为内部知识库问答机器人接入Taotoken多模型增强回答效果 构建一个高效的企业内部知识库问答机器人,核心挑战在于如何让…...
【C语言】C 语言为什么叫 C 语言呢?
【C语言】C 语言为什么叫 C 语言呢?笔记改自于王道训练营资料 其实是因为先有高级语言ALGOL 60,简称 A 语言,后来经过简化,变为 BCPL 语言,简称 B 语言,而 C 语言是在 B 语言的基础之上发展而来的ÿ…...
终极免费音乐解锁工具:打破平台枷锁,让音乐重获自由
终极免费音乐解锁工具:打破平台枷锁,让音乐重获自由 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地…...
LeetCode 80 · 删除有序数组中的重复项 II:通用模板的威力
LeetCode 26 要求每个元素最多出现一次,这道题放宽到最多出现两次。看起来只是把 1 改成了 2,但这个"小改动"背后藏着一个通用的快慢指针模板——把 2 换成任意整数 m,代码几乎不用动。这就是模板的威力:改一个数字&…...
