微信小程序调用 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 线程详解
目录 一、线程概述 二、线程创建 三、线程终止 四、线程回收 五、线程取消 六、线程分离 七、线程安全 一、线程概述 线程是进程内的一个执行单元,是进程内可调度的实体。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空…...
云架构:考量与框架
云架构:考量与框架 引言 在当今的数字化环境中,云计算已成为现代商业运营的基石。一个设计良好的云架构框架为可扩展、安全和弹性的系统奠定了基础。本文将深入探讨云架构的核心要素,讨论重要的考量因素、设计指南,以及最佳实践…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...