Vue调用wasm使用说明

一、需求及背景描述

​ 因现有的前后端交互大多是依赖c++的计算,想把后台的c++库的部分计算移到浏览器当中实现,因为受制于沙盒的缘故前端无法直接调用C++的dll。所以使用一种新的解决方案,使用WebAssembly把C++源码编译成wasm文件,前端可以调用wasm中封装好的方法。

WebAssembly:WebAssembly/wasm WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。也 是由主流浏览器厂商组成的 W3C 社区团体 制定的一个新的规范

官网链接:http://webassembly.org.cn/

二、环境搭建

​ C/C++主要依赖于Emscripten环境来编译到WebAssembly。windows下配置Emscripten环境易错且繁杂,所以社区比较推荐linux下安装,或者win环境下通过Docker快速搭建WebAssembly编译环境。

(C++开发人员转好wasm后发送给前端,前端可不配置本机的Emscripten环境,或者前端使用在线转换也不需配置emscripten环境)

Emscripten 环境安装

你需要将下列工具安装在您的电脑上,首先让我们确认下都有哪些。

**注意:**在 Windows 下您可能需要pywin32,为了降低安装 pywin32 可能遇到的错误,请使用管理员权限在 cmd 内运行安装程序。

安装完毕后,确认 gitcmakepython 已经在你的环境变量里,可以使用。

接下来,您需要通过源码自己编译一个 Emscripten。运行下列命令来自动化地使用 Emscripten SDK。(在你想保存 Emscripten 的文件夹下运行)。

  • #从GitHub仓库下载编译器项目文件,选择一个英文目录,打开git命令行
git clone https://github.com/juj/emsdk.git
  • 下载成功后,当前目录下会出现一个emsdk目录,进入到该目录下。
cd emsdk
  • 安装最新的SDK并激活,在当前命令行继续输入命令。 注: 安装要一点时间,需要耐心等待,具体速度看网络情况。
# 在 Linux 或者 Mac macOS 上
./emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
./emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit
# 如果在你的 macos 上获得以下错误
Error: No tool or SDK found by name 'sdk-incoming-64bit'
# 请执行
./emsdk install latest
# 按照提示配置环境变量即可
./emsdk activate latest

# 在 Windows 上
emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit
  • 在命令行输入em++ -v测试编译器是否安装成功。

此时环境安装已完成

安装文档可参考:https://emscripten.org/docs/getting_started/downloads.html

Win10使用Docker搭建环境可参考:https://www.jianshu.com/p/a35aa4e26831

开发者引导文档:http://webassembly.org.cn/getting-started/developers-guide/

三、C/C++转换wasm

  1. emcc转换
  • C++在编写代码时,需要对外进行函数暴露
#ifdef __cplusplus
extern "C"
{
#endif
   
    int fun(int x) {
        if (x <= 0)
            return 0;
        if (x <= 2)
            return 1;
        return fun(x - 1) + fun(x - 2);
    }

#ifdef __cplusplus
}
#endif

  • 转换wasm
//进入到Cpp文件所在目录,打开终端执行
emcc fun.cpp -s "EXPORTED_FUNCTIONS=['_fun']" -o fibtest.js

其中fun.cpp为我的C++文件名称

-s后跟的是编译时的选项:

  • EXPORTED_FUNCTIONS值需要暴露给js调用的自定义函数,对应Cpp文件中的函数,在其函数名前前加一个下划线

-o后跟的是编译目标,我们生成一个js文件,其中会附带帮助我们运行wasm的“胶水代码”

**注意:**在执行emcc命令时,在暴露的函数名称fun前加下划线,如“_fun"

若暴露多个函数,只需在EXPORTED_FUNCTIONS增加

emcc fun.cpp -s "EXPORTED_FUNCTIONS=['_fun','_fun2','_fun3']" -o fibtest.js
  1. 在线转换

    emcc转换后的wasm文件比较大,如果源码不太复杂的话,也可以在线转换。在线转换网站https://mbebenita.github.io/WasmExplorer/。

    转换流程请看下图,转换后的wasm大小为1kb

在这里插入图片描述

注意:转换后接口函数名已发生变化,前端需要调用新的函数名,或者手动进行更改。

C语言的在线转换网站:https://wasdk.github.io/WasmFiddle/

四、Vue中调用wasm

为了打包后wasm也能被找到,建议将wasm放到public目录下,并在目录下创建js/wasm目录,将wasm文件都保存在此。(或者有更好的方式)

如果控制台报如下错误,是因为wasm文件未被找到。

Failed to load resource: the server responded with a status of 404 (Not Found)
Uncaught (in promise) CompileError: WebAssembly.compile(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0

vue调用wasm实现代码

   fetch('js/wasm/fib.wasm').then(response=>
          response.arrayBuffer()).then(bytes =>WebAssembly.compile(bytes)).then(mod=>{
        const instance = new WebAssembly.Instance(mod)
        const a= instance.exports
        //_Z3fibi(3)为函数。
        console.log(a._Z3fibi(3))
      });

也可封装一个通用的加载函数

 loadWebAssembly(path, imports = {}) {
      return fetch(path) // 加载文件
      .then(response => response.arrayBuffer()) // 转成 ArrayBuffer
      .then(buffer => WebAssembly.compile(buffer))
      .then(module => {
        imports.env = imports.env || {}
        // 开辟内存空间
        imports.env.memoryBase = imports.env.memoryBase || 0
        if (!imports.env.memory) {
          imports.env.memory = new WebAssembly.Memory({ initial: 256 })
        }
        // 创建变量映射表
        imports.env.tableBase = imports.env.tableBase || 0
        if (!imports.env.table) {
          // 在 MVP 版本中 element 只能是 "anyfunc"
          imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
        }
        // 创建 WebAssembly 实例
        return new WebAssembly.Instance(module, imports)
      })
}
#函数调用
 this.loadWebAssembly ('js/wasm/math.wasm')
          .then(instance => {
            const square = instance.exports.square//取出cpp里面的方法
            this.result=square(this.num)
          })
Logo

前往低代码交流专区

更多推荐