如何在create-react-app中使用WebAssembly(wasm)

12

我使用 create-react-app 和一个名为 sax-wasm 的包含 wasm 的库。稍微修改了网络示例代码后,我得到了如下内容:

import { SaxEventType, SAXParser } from 'sax-wasm';

async function loadAndPrepareWasm() {
  const saxWasmResponse = await import('sax-wasm/lib/sax-wasm.wasm');
  const saxWasmbuffer = await saxWasmResponse.arrayBuffer();
  const parser = new SAXParser(SaxEventType.Attribute | SaxEventType.OpenTag, {
    highWaterMark: 64 * 1024,
  });

  const ready = await parser.prepareWasm(new Uint8Array(saxWasmbuffer));
  if (ready) {
    return parser;
  }
}

loadAndPrepareWasm().then(console.log);
当我运行yarn start命令时,我的构建失败了。
Failed to compile.

./node_modules/sax-wasm/lib/sax-wasm.wasm
Module parse failed: magic header not detected
File was processed with these loaders:
 * ./node_modules/file-loader/dist/cjs.js
You may need an additional loader to handle the result of these loaders.
Error: magic header not detected

我认为这个上游问题链接是跟踪/点赞的适当来源。 - bluenote10
2个回答

5
以下解决方案适用于CRA 3.x。对于CRA 4.x,您应该使用craco
CRA不支持WASM。但是您可以绕过它。您需要覆盖webpack配置。有多个软件包可以做到这一点。我使用了react-app-rewired。还需要wasm-loader软件包。
然后添加您的覆盖config-overrides.js:
const path = require('path');

module.exports = function override(config, env) {

    /**
     * Add WASM support
     */

    // Make file-loader ignore WASM files
    const wasmExtensionRegExp = /\.wasm$/;
    config.resolve.extensions.push('.wasm');
    config.module.rules.forEach(rule => {
        (rule.oneOf || []).forEach(oneOf => {
            if (oneOf.loader && oneOf.loader.indexOf('file-loader') >= 0) {
                oneOf.exclude.push(wasmExtensionRegExp);
            }
        });
    });

    // Add a dedicated loader for WASM
    config.module.rules.push({
        test: wasmExtensionRegExp,
        include: path.resolve(__dirname, 'src'),
        use: [{ loader: require.resolve('wasm-loader'), options: {} }]
    });

    return config;
};

package.json 文件中,我是这样做的:

{
...
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject",
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  },
...
  "devDependencies": {
    "react-app-rewired": "2.1.3",
    "wasm-loader": "1.3.0"
  }
}

1
非常感谢!对我来说解决方案只有一半。我只需要让文件加载器忽略.wasm文件即可。当我添加一个专门的WASM加载器时,它不起作用。此外,将“.wasm”文件的“type”设置为“javascript/auto”对我也没有用。(我正在使用wasm-bindgen) - David Sherret
注意:为了能够加载wasm文件,您的Web服务器需要支持wasm,并返回wasm MIME类型作为application/wasm。 - sancelot
对我来说,wasm-loader 需要被替换为 file-loader - Will Squire
这是什么意思?你首先说某人需要使用craco,然后又展示了某人不需要使用craco?我感到困惑。我只想编写我的应用程序,这太令人沮丧了。请在您的评论中明确清晰。 - jdbertron

0

你最终想要在浏览器中使用wasm。这意味着你必须在浏览器中访问它。

我安装了esbuild-wasm。有两种设置方法:

1- 在node模块中找到模块“node_modules/esbuild-wasm”,你会看到一个文件“esbuild-wasm”,并将其放置在public文件夹中。这个编译好的代码可以直接在浏览器中运行。在组件内部:

import * as esbuild from "esbuild-wasm"
import {useRef, useState} from "react"

// you can use ref to keep a reference to any Js value inside of a component. 
const ref=useRef()
const startEsbuildService=async()=>{
   // this object is responsible for transpiling
   ref.current=await esbuild.startService({
   worker:true,
   // we are pointing to the public directory
   wasmURL:'/esbuild.wasm'})
}

第二种方法:

const startEsbuildService = async () => {
    ref.current = await esbuild.startService({
      worker: true,
      // unpkg.com provides all npm modules without CORS error
      wasmURL: "https://unpkg.com/esbuild-wasm@0.8.42/esbuild.wasm",
    });
  };

这是如何使用它

 // you store the code
    const [codeInput,setCodeInput]=useState("")
    const [transpiledCode,setTranspiledCode]=useState("")
const onClick= async ()=>{
   if(!ref.current) return;
   const transformedCode=await ref.current.transform(codeInput,{
      // es-build can handle different types of code
      loader:'jsx',
      target: 'es2015'})
   // this will rerender our component
   setTranspiledCode(transformedCode)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接