微信小程序调用 WebAssembly 烹饪指南
我们都是在夜里崩溃过的俗人,所幸终会天亮。明天就是新的开始,我们会变得与昨天不同。
一、Rust 导出 wasm
参考 wasm-bindgen 官方指南 https://wasm.rust-lang.net.cn/wasm-bindgen/introduction.html
wasm-bindgen,这是一个 Rust 库和 CLI 工具,它可以促进 wasm 模块和 JavaScript 之间的高级交互。
1、创建一个 wasm 项目
# 使用模板生成
# cargo generate --git https://gitee.com/tgodfather/wasm-pack-template
cargo generate --git https://github.com/rustwasm/wasm-pack-template

2、添加 js-sys 依赖
cargo add js-sys
3、修改 lib.rs
定义三个外部函数,它们分别对应于 JavaScript 中的 console.log、console.error 和 wx.showModal。通过使用 wasm_bindgen,这些函数可以在 Rust 代码中被调用,从而实现与 JavaScript 的交互。
#[wasm_bindgen]
extern "C" {#[wasm_bindgen(js_namespace = console)]fn log(s: &str);#[wasm_bindgen(js_namespace = console)]fn error(s: &str);#[wasm_bindgen(js_namespace = wx)]fn showModal(param: &Object);
}
使用 rust 分别调用这三个函数,导出为 wasm 函数,
#[wasm_bindgen]
pub fn rs_log() {log("log");
}#[wasm_bindgen]
pub fn rs_error() {log("error");
}#[wasm_bindgen]
pub fn rs_show_modal() {// 创建一个 JavaScript 对象let options = Object::new();// 设置对象的属性Reflect::set(&options,&JsValue::from_str("title"),&JsValue::from_str("提示"),).unwrap();Reflect::set(&options,&JsValue::from_str("content"),&JsValue::from_str("这是一个模态弹窗"),).unwrap();// 创建回调函数let success_callback = Closure::wrap(Box::new(|res: JsValue| {let confirm = Reflect::get(&res, &JsValue::from_str("confirm")).unwrap().as_bool().unwrap_or(false);let cancel = Reflect::get(&res, &JsValue::from_str("cancel")).unwrap().as_bool().unwrap_or(false);if confirm {log("用户点击确定");} else if cancel {log("用户点击取消");}}) as Box<dyn FnMut(_)>);// 将回调函数添加到对象中Reflect::set(&options,&JsValue::from_str("success"),success_callback.as_ref().unchecked_ref(),).unwrap();// 为了避免回调被回收,必须调用 `forget`success_callback.forget();// 调用 JavaScript 的 `showModal` 函数showModal(&options);
}
补充:小程序官方弹窗示例代码,
wx.showModal({title: '提示',content: '这是一个模态弹窗',success (res) {if (res.confirm) {console.log('用户点击确定')} else if (res.cancel) {console.log('用户点击取消')}}
})
4、编译打包 wasm
wasm-pack build --target web


可以看到在 pkg 目录下生成了我们需要用到的 mywasm_bg.wasm、mywasm.js 。
二、对应小程序相关改动(Rust)
对于微信小程序,直接编译打包后的包无法直接调用,所以还需要进行一些代码修改。
WXWebAssembly | 微信开放文档
1、新增目录
新增 workers 目录:与 pages 同级,创建 workers 目录,用于存放 .wasm 文件
workers 目录只存放 mywasm_bg.wasm,便于把.wasm打包进去,以及分包打包
新增 pages/worker 目录:用于进行打包文件的调用
pages/worker目录只存放 .js ,这个文件包含了一些调用 .wasm 文件的方法
2、修改 mywasm.js 胶水代码
#改动点1、注释原 __wbg_load 方法逻辑,替换使用以下代码async function __wbg_load(module, imports) {if (typeof Response === 'function' && module instanceof Response) {const bytes = await module.arrayBuffer();return await instantiateArrayBuffer(bytes, imports);} else {return await instantiateArrayBuffer(module, imports);}
}
# 改动点2、手动指定 WebAssembly 模块的路径// 手动指定 WebAssembly 模块的路径
const wasmModulePath = '/workers/mywasm_bg.wasm';async function instantiateArrayBuffer(binaryFile, imports) {return WXWebAssembly.instantiate(wasmModulePath, imports).then(function(instance) {return instance;}).catch(function(reason) {console.error('Failed to asynchronously prepare wasm: ' + reason);throw reason;});
}
# 改动点3、手动指定 WebAssembly 模块的路径if (typeof module_or_path === 'undefined') {// module_or_path = new URL('mywasm_bg.wasm',// import.meta.url);module_or_path = wasmModulePath;}if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {// 需要使用 wx.request 替代 fetch // module_or_path = fetch(module_or_path);module_or_path = wasmModulePath;}
3、调用 wasm
import init,{ rs_log ,rs_error,rs_show_modal} from '../worker/mywasm.js';
onLoad: async function () {try {await init();// 使用 wasmModule 中的导出函数rs_log();rs_error();rs_show_modal();} catch (e) {console.error('Failed to load WASM module:', e);}}
4、运行效果

从控制台日志输出可以看到,wasm 导出的函数运行成功。
三、C/C++ 导出 wasm
1、创建一个 hellojs.cpp
#include <stdio.h>
#include <emscripten/emscripten.h>int main(int argc, char ** argv) {printf("Hello World\n");
}#ifdef __cplusplus
extern "C" {
#endifint EMSCRIPTEN_KEEPALIVE myFunction(int argc, char ** argv) {printf("我的函数已被调用\n");
}#ifdef __cplusplus
}
#endif
2、使用 emcc 编译打包
# emcc -o hellojs.html hellojs.cpp -O3 -s WASM=1 -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" --shell-file html_template/shell_minimal.htmlemcc -o hellojs.html hellojs.cpp -O3 -s WASM=1 -s NO_EXIT_RUNTIME=1 -s "EXPORTED_RUNTIME_METHODS=['ccall']" --shell-file html_template/shell_minimal.html

成功生成我们所需要的 hellojs.js、hellojs.wasm 文件。
四、对应小程序相关改动(C++)
1、新增目录
新增 workers 目录:与 pages 同级,创建 workers 目录,用于存放 .wasm 文件
workers 目录只存放 mywasm_bg.wasm,便于把.wasm打包进去,以及分包打包
新增 pages/worker 目录:用于进行打包文件的调用
pages/worker目录只存放 .js ,这个文件包含了一些调用 .wasm 文件的方法

2、修改 hellojs.js 胶水代码
# 改动1、在文件的最底部添加
module.exports = {Module: Module
}
# 改动2、注释代码
if(ENVIRONMENT_IS_WORKER) {// scriptDirectory=self.location.href}
# 改动3、修改instantiateArrayBuffer函数// function instantiateArrayBuffer(binaryFile, imports, receiver) {
// return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary, imports)).then(receiver, reason=> {
// err(`failed to asynchronously prepare wasm: ${reason}`); abort(reason)
// })
// }
function instantiateArrayBuffer(binaryFile, imports, receiver) {return WXWebAssembly.instantiate('/workers/hellojs.wasm', imports).then(function(instance) {return instance;}).then(receiver, function(reason) {err('failed to asynchronously prepare wasm: ' + reason);// Warn on some common problems.if (isFileURI(wasmBinaryFile)) {err('warning: Loading from a file URI (' + wasmBinaryFile + ') is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing');}abort(reason);})
}
# 改动4、将js文件中的所有WebAssembly修改为WXWebAssemblyfunction abort(what) {Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";//var e=new WebAssembly.RuntimeError(what);var e=new WXWebAssembly.RuntimeError(what);throw e
}function instantiateAsync(binary, binaryFile, imports, callback) {if( !binary&&typeof WXWebAssembly.instantiateStreaming=="function" && !isDataURI(binaryFile)&& !isFileURI(binaryFile)&& !ENVIRONMENT_IS_NODE&&typeof fetch=="function") {return fetch(binaryFile, {credentials:"same-origin"}).then(response=> {var result=WXWebAssembly.instantiateStreaming(response, imports); return result.then(callback, function(reason) {err(`wasm streaming compile failed: ${reason}`); err("falling back to ArrayBuffer instantiation"); return instantiateArrayBuffer(binaryFile, imports, callback)})})
}return instantiateArrayBuffer(binaryFile, imports, callback)
}
3、调用 wasm
# 引入文件
const hellojs_wasm = require('../worker/hellojs');
onReady() {const moudule = hellojs_wasm.Module// moudule._myFunction(1,"");moudule.ccall("myFunction", // name of C functionnull, // return typenull, // argument typesnull,); // arguments},
4、运行效果

从控制台日志输出可以看到,wasm 导出的函数运行成功。
五、参考资料
基于 Rust 的 Wasm 开发探索与实践_rust wasm-CSDN博客
基于 Rust 的 Wasm/Wasi 开发探索与实践(Linux开发环境)_wasi安装-CSDN博客
基于 Emscripten + OpenXLSX 实现浏览器操作 Excel_使用webassembly在浏览器端操作excel-CSDN博客
相关文章:
微信小程序调用 WebAssembly 烹饪指南
我们都是在夜里崩溃过的俗人,所幸终会天亮。明天就是新的开始,我们会变得与昨天不同。 一、Rust 导出 wasm 参考 wasm-bindgen 官方指南 https://wasm.rust-lang.net.cn/wasm-bindgen/introduction.html wasm-bindgen,这是一个 Rust 库和 CLI…...
# LeetCode Problem 2038: 如果相邻两个颜色均相同则删除当前颜色 (Winner of the Game)
LeetCode Problem 2038: 如果相邻两个颜色均相同则删除当前颜色 (Winner of the Game) 在本篇博客中,我们将深入探讨 LeetCode 第2038题——如果相邻两个颜色均相同则删除当前颜色。该问题涉及字符串处理与游戏策略,旨在考察如何在给定规则下判断游戏的…...
Redis面试相关
Redis开篇 使用场景 缓存 缓存穿透 解决方法一: 方法二: 通过多次hash来获取对应的值。 小结 缓存击穿 缓存雪崩 打油诗 双写一致性 两种不同的要求 强一致 读锁代码 写锁代码 强一致,性能低。 延迟一致 方案一:消息队列 方…...
4.CSS文本属性
4.1文本颜色 div { color:red; } 属性值预定义的颜色值red、green、blue、pink十六进制#FF0000,#FF6600,#29D794RGB代码rgb(255,0,0)或rgb(100%,0%,0%) 4.2对齐文本 text-align 属性用于设置元素内文本内容的水平对齐方式。 div{ text-align:center; } 属性值解释left左对齐ri…...
Mongo高可用架构解决方案
Mongo主从复制哪些事(仅适用特定场景) 对数据强一致性要求不高的场景,一般微服务架构中不推荐 master节点可读可写操作,当数据有修改时,会将Oplog(操作日志)同步到所有的slave节点上。那么对于从节点来说仅只读,所有slave节点从master节点同步数据,然而从节点之间互相…...
Rabbitmq 业务异常与未手动确认场景及解决方案
消费端消费异常,业务异常 与 未手动确认是不是一个场景,因为执行完业务逻辑,再确认。解决方案就一个,就是重试一定次数,然后加入死信队列。还有就是消费重新放入队列,然后重新投递给其他消费者,…...
linux,centos7.6安装禅道
1.cd /opt 2.wget https://www.zentao.net/dl/zentao/18.5/ZenTaoPMS.18.5.zbox_64.tar.gz 3.tar xvzf ZenTaoPMS.18.5.zbox_64.tar.gz 4./opt/zbox/zbox --aport 4005 --mport 3307 start 安全组开启一下两个端口 –aport 4005:设置Apache启动端口为4005 –mport 3…...
java基础之代理
代理模式(Proxy Pattern) 简介 是一种结构型设计模式,主要用于为某对象提供一个代理对象,以控制对该对象的访问。通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的…...
计算机网络——期末复习(6)期末考试样例2(含答案)
一、单项选择题(每题1分,共10小题,共计10分) 1.因特网多播采用的管理协议是( )协议。 A.UDP B.BGP C.IGMP D.TCP 2.采用CIDR时,某计算机的IP地址是202.35.79.88/19,该计算机所处子网的地址是( )。 A.202.35.0.…...
JavaScript 获取DOM对象
html的标签被js获取到之后就变成了js对象,对象里面包含了标签的属性和方法 。同一时间获取多个对象则会翻译一个数组,数组元素是对象 获取方法 1. const a document.getElementById("id"),根据标签的id来获取。因为id是唯一的、…...
一文讲明白朴素贝叶斯算法及其计算公式(入门普及)
1、贝叶斯算法 贝叶斯定理由英国数学家托马斯贝叶斯 ( Thomas Bayes) 提出的,用来描述两个条件概率之间的关系。通常,事件A在事件B 发生的条件下与事件 B 在事件 A 发生的条件下,它们两者的概率并不相同,但是它们两者之间存在一定…...
实际开发中,常见pdf|word|excel等文件的预览和下载
实际开发中,常见pdf|word|excel等文件的预览和下载 背景相关类型数据之间的转换1、File转Blob2、File转ArrayBuffer3、Blob转ArrayBuffer4、Blob转File5、ArrayBuffer转Blob6、ArrayBuffer转File 根据Blob/File类型生成可预览的Base64地址基于Blob类型的各种文件的下载各种类型…...
Python自学 - 递归函数
1 Python自学 - 递归函数 递归函数是一种在函数体内调用自己的函数,就像“左脚踩着右脚,再右脚踩着左脚… 嗯,你就可以上天了!”。递归函数虽然不能上天,但在处理某些场景时非常好用, 一种典型的场景就是遍…...
Spark-Streaming有状态计算
一、上下文 《Spark-Streaming初识》中的NetworkWordCount示例只能统计每个微批下的单词的数量,那么如何才能统计从开始加载数据到当下的所有数量呢?下面我们就来通过官方例子学习下Spark-Streaming有状态计算。 二、官方例子 所属包:org.…...
Markdown如何导出Html文件Markdown文件
Markdown如何导出Html文件Markdown文件 前言语法详解小结其他文章快来试试吧☺️ Markdown 导出 HTML 👈点击这里也可查看 前言 Markdown的源文件以md为后缀。Markdown是HTML语法的简化版本,它本身不带有任何样式信息。我们所看到的Markdown网页(如&…...
使用Python进行图像裁剪和直方图分析
一、简介 在数字图像处理领域,裁剪和分析图像的直方图是两个非常基本且重要的操作。本文将通过一个简单的Python项目,展示如何使用skimage和matplotlib库来裁剪图像并分析其RGB通道的直方图。 二、环境准备 在开始之前,请确保你已经安装了以…...
企业内管信息化系统
本文结尾处获取源码。 本文结尾处获取源码。 本文结尾处获取源码。 一、相关技术 后端:Java、JavaWeb / Springboot。前端:Vue、HTML / CSS / Javascript 等。数据库:MySQL 二、相关软件(列出的软件其一均可运行) I…...
【python因果库实战15】因果生存分析4
这里写目录标题 加权标准化生存分析总结个体层面的生存曲线 加权标准化生存分析 我们还可以将加权与标准化结合起来,使用 WeightedStandardizedSurvival 模块。在这里,我们将逆倾向得分加权模型(根据基线协变量重新加权人群)与加…...
Linux 线程详解
目录 一、线程概述 二、线程创建 三、线程终止 四、线程回收 五、线程取消 六、线程分离 七、线程安全 一、线程概述 线程是进程内的一个执行单元,是进程内可调度的实体。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空…...
云架构:考量与框架
云架构:考量与框架 引言 在当今的数字化环境中,云计算已成为现代商业运营的基石。一个设计良好的云架构框架为可扩展、安全和弹性的系统奠定了基础。本文将深入探讨云架构的核心要素,讨论重要的考量因素、设计指南,以及最佳实践…...
5分钟快速上手:Nexus Mods App模组管理器终极指南
5分钟快速上手:Nexus Mods App模组管理器终极指南 【免费下载链接】NexusMods.App Home of the development of the Nexus Mods App 项目地址: https://gitcode.com/gh_mirrors/ne/NexusMods.App 如果你厌倦了手动安装游戏模组、解决冲突依赖的繁琐过程&…...
5分钟搞定BAAI/bge-m3环境配置:sentence-transformers框架调优指南(小白版)
5分钟搞定BAAI/bge-m3环境配置:sentence-transformers框架调优指南(小白版) 1. 为什么你需要关注BAAI/bge-m3? 如果你正在做智能客服、文档检索、或者想搭建自己的知识库系统,那你一定遇到过这样的问题:用…...
实战分享怎样实现IntelliJ IDEA 打包 Web 项目 WAR 包(含 Tomcat 部署 + 常见问题解决)
在 Java Web 开发中,“本地能跑”只是第一步,真正让很多人头疼的是后续这条链路: 项目打包 → 生成 WAR → 部署 Tomcat → 启动验证 → 排查报错。尤其是刚从 Spring Boot 内嵌容器模式转向传统 WAR 部署、或者接手老项目时,常常…...
为什么很多企业买了大模型,最后还是落不了地?
很多企业花重金采购了大模型的 API,甚至买了昂贵的算力服务器,满心欢喜地以为迎来了生产力革命,结果三个月后,系统却沦为员工写请假条和废话周报的“高级打字机”。为什么技术如此先进,在企业内部却寸步难行࿱…...
Spring AI ETL进阶:定制中文元数据增强与Milvus向量化存储实战
1. Spring AI ETL的核心价值与应用场景 在处理中文文本数据时,传统的ETL流程常常会遇到语义理解不准确、上下文丢失等问题。Spring AI提供的ETL框架通过模块化设计,让开发者能够轻松构建适合中文场景的数据处理流水线。我最近在一个知识库项目中实际应用…...
TranslucentTB启动失败?3步修复Microsoft.UI.Xaml依赖问题
TranslucentTB启动失败?3步修复Microsoft.UI.Xaml依赖问题 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB TranslucentTB是一…...
Nano-Banana镜像免配置部署:Docker+Streamlit极简交互环境搭建
Nano-Banana镜像免配置部署:DockerStreamlit极简交互环境搭建 1. 引言:让结构拆解变得简单高效 如果你是一名设计师、工程师或创意工作者,一定遇到过这样的需求:需要将复杂的产品拆解成清晰的部件展示图。传统方法需要专业的3D建…...
Pixel Epic · Wisdom Terminal 物联网(IoT)后端开发:处理海量传感器数据的智能分析平台
Pixel Epic Wisdom Terminal 物联网(IoT)后端开发:处理海量传感器数据的智能分析平台 1. 工业物联网的智能化挑战 在工业制造领域,每天都有数以万计的传感器源源不断地产生数据。这些数据就像工厂的"神经末梢"&#…...
Java 高并发场景下 Redis 分布式锁(UUID+Lua)最佳实践
一、核心原理:Redis 分布式锁的设计基石1.1 分布式锁的核心要求一款可靠的分布式锁需满足以下 4 点核心要求,否则易引发死锁、锁误删、数据不一致等问题:互斥性:同一时间只有一个线程能持有锁,杜绝并发竞争;…...
拼多多如何批量上下架商品?拼多多一键下架所有商品操作步骤
拼多多商家怎么进行上下架商品的操作?怎么设置商品预售?拼多多一键下架所有商品操作步骤,下面来一步一步说明一下:一、上下架商品:1.进入拼多多商家版后台,找到【商品管理】--【商品列表】选项并点击&#…...

