javascript - 如何在 vue.js 中调用 webassembly 方法?

标签 javascript c vue.js emscripten webassembly

我正在尝试将这个简单的 html 页面 add.html 转换为 vue.js:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
  </head>
  <body>
    <input type="button" value="Add" onclick="callAdd()" />

    <script>
      function callAdd() {
        const result = Module.ccall('Add',
            'number',
            ['number', 'number'],
            [1, 2]);

        console.log(`Result: ${result}`);
      }
    </script>
    <script src="js_plumbing.js"></script>
  </body>
</html>

调用 add.c 中定义的 Add 函数:

#include <stdlib.h>
#include <emscripten.h>

// If this is an Emscripten (WebAssembly) build then...
#ifdef __EMSCRIPTEN__
  #include <emscripten.h>
#endif

#ifdef __cplusplus
extern "C" { // So that the C++ compiler does not rename our function names
#endif

EMSCRIPTEN_KEEPALIVE
int Add(int value1, int value2) 
{
  return (value1 + value2); 
}

#ifdef __cplusplus
}
#endif

并通过命令转换为js_plumbing和js_plumbling.wasm文件:

emcc add.c -o js_plumbing.js -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -s 
ENVIRONMENT='web','worker'

在 google chrome 的控制台中我得到这些错误:

GET http://localhost:8080/dist/js_plumbing.wasm 404 (Not Found)  @  js_plumbing.js?2b2c:1653

js_plumbing_js 中的位置:

// Prefer streaming instantiation if available.
  function instantiateAsync() {
    if (!wasmBinary &&
        typeof WebAssembly.instantiateStreaming === 'function' &&
        !isDataURI(wasmBinaryFile) &&
        typeof fetch === 'function') {
      fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) {  // <---------------!!!
        var result = WebAssembly.instantiateStreaming(response, info);
        return result.then(receiveInstantiatedSource, function(reason) {
            // We expect the most common failure cause to be a bad MIME type for the binary,
            // in which case falling back to ArrayBuffer instantiation should work.
            err('wasm streaming compile failed: ' + reason);
            err('falling back to ArrayBuffer instantiation');
            instantiateArrayBuffer(receiveInstantiatedSource);
          });
      });
    } else {
      return instantiateArrayBuffer(receiveInstantiatedSource);
    }
  }

在谷歌浏览器中:createWasm @js_plumbing.js?2b2c:1680

js_plumbing.js 的第 1680 行:

instantiateAsync();

在谷歌浏览器中:eval @ js_plumbing.js?2b2c:1930

js_plumbing.js 的第 1930 行:

<pre><font color="#4E9A06">var</font> asm = createWasm();</pre>

以及与 wasm 相关的许多其他错误:

enter image description here

enter image description here

那么...我应该如何修改 Result.vue 中的 callAdd() 方法才能正确执行 js_plumbing.js 和 js_plumbing.wasm 文件中的 Add 函数?

  methods: {
    callAdd() {
      const result = Module.ccall('Add',
          'number',
          ['number', 'number'],
          [1, 2]);
      console.log('Result: ${result}');
    }
  }

更新:

1 次更新)

我用这个命令编译了 add.c:

emcc add.c -o js_plumbing.mjs -s EXTRA_EXPORTED_RUNTIME_METHODS=
['ccall','cwrap'] -s ENVIRONMENT='web' . 

然后创建一个 js_plumbing.js 文件:

. import wasm from './js_plumbing.mjs';

const instance = wasm({
  onRuntimeInitialized() {
    console.log(instance._addTwoNumbers(3,2));
  }
}) . 

执行 npm run dev:

Failed to compile.

./src/components/js_plumbing.mjs 3:25
Module parse failed: Unexpected token (3:25)
You may need an appropriate loader to handle this file type, currently 
no loaders are configured to process this file. 
See https://webpack.js.org/concepts#loaders
| 
| var Module = (function() {
>   var _scriptDir = import.meta.url;
|   
|   return (

更新 2)

我通过将 wasm 文件放入 index.html 文件同一文件夹内的/div 子文件夹解决了 404 错误。

现在我面临这个问题:“无法读取未定义的属性‘ccall’” enter image description here

但我编译了 add.c 文件,创建了 js_plumbing.js 和 js_plumbing.wasm 文件,使用这个命令导出方法‘ccall’和‘cwrap’:

emcc add.c -o js_plumbing.js -s EXTRA_EXPORTED_RUNTIME_METHODS=[‘ccall’,‘cwrap’] -s ENVIRONMENT=‘web’,‘worker’

3°更新)

我通过一种我根本不喜欢的黑客手段“解决”了问题。

这是 Result.vue 文件:

<template>
  <div>
    <p button @click="callAdd">Add!</p>
    <p>Result: {{ result }}</p>
  </div>
</template>

<script>
    import * as js_plumbing from './js_plumbing'
    import Module  from './js_plumbing'
    export default {
      data () {
        return {
          result: null
        }
      },
      methods: {
        callAdd () {
          const result = js_plumbing.Module.ccall('Add',
            'number',
            ['number', 'number'],
            [1, 2]);
          this.result = result;
        }
      }
    }
</script>

和之前用的一模一样

我为让它工作所做的唯一一件事就是将导出添加到 js_plumbing.js 中的 Module 定义:

js_plumbing.js

// Copyright 2010 The Emscripten Authors.  All rights reserved.
// Emscripten is available under two separate licenses, the MIT 
license and the
// University of Illinois/NCSA Open Source License.  Both these 
licenses can be
// found in the LICENSE file.

// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(Module) { ..generated code.. }
// 3. pre-run appended it, var Module = {}; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of 
the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define   var Module = 
{};
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
export var Module = typeof Module !== 'undefined' ? Module : {};

enter image description here

但是,正如我所说,我不喜欢这种 hack。 关于如何使模块可导出,从而可导入,而无需在 js_plumbing.js 文件中手动添加“导出”的任何建议?

最佳答案

首先,应该解决 404 错误。文件 /dist/js_plumbing.wasm 是否存在?我过去需要手动复制 .wasm 文件,因为一些自动构建系统(如 Parcel)目前不需要。

您可以使用 MODULARIZE 选项构建以导入您的构建系统。

addTwoNumbers.c

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int addTwoNumbers(int value1, int value2) 
{
  return (value1 + value2); 
}

构建命令

$ emcc -o dist/addTwoNumbers.js -s MODULARIZE=1 src/addTwoNumbers.c

Vue 实现

import myMathModule from './js_plumbing';

let instance = {
  ready: new Promise(resolve => {
    myMathModule({
      onRuntimeInitialized() {
        instance = Object.assign(this, {
          ready: Promise.resolve()
        });
        resolve();
      }
    });
  })
};

export default {
  data () {
    return {
      result: null
    };
  },
  methods: {
    callAdd(a, b) {
      instance.ready
      .then(_ => this.result = instance._add(a, b));
    }
  }
};

使用 onRuntimeInitialized 方法检测 WASM 模块何时准备就绪。您导出的函数前面会有一个下划线。

require() 可以用来代替 import:

const wasmModule = require('./addTwoNumbers.js');

...

关于javascript - 如何在 vue.js 中调用 webassembly 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59500326/

相关文章:

根据 ASCII 优先级比较字符

vue.js - 登录后 NuxtJS 重定向

c - 如何在内核中放置微秒延迟?

javascript - 在 Vue.js 中实现商店模式

vue.js - Vuejs 图像 src 动态加载不起作用

javascript - 在 JavaScript 中格式化 PHP 字符串

javascript - 字符串替换不适用于从 XMLHttpRequest 请求返回的值

javascript - 在输入焦点上显示跨度

javascript - 从 JavaScript 对象中检索所有值

c - ASM x64 scanf printf double, GAS