DEV Community

lencx
lencx

Posted on

8 3

WebAssembly入门

Wasm是什么?

MDN官方文档是这样给出定义

WebAssembly(为了书写方便,简称Wasm)是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如C / C ++等语言提供一个编译目标,以便它们可以在Web上运行。它也被设计为可以与JavaScript共存,允许两者一起工作。

对于网络平台而言,WebAssembly具有巨大的意义——它提供了一条途径,以使得以各种语言编写的代码都可以以接近原生的速度在Web中运行。在这种情况下,以前无法以此方式运行的客户端软件都将可以运行在Web中。

WebAssembly被设计为可以和JavaScript一起协同工作——通过使用WebAssembly的JavaScript API,你可以把WebAssembly模块加载到一个JavaScript应用中并且在两者之间共享功能。这允许你在同一个应用中利用WebAssembly的性能和威力以及JavaScript的表达力和灵活性,即使你可能并不知道如何编写WebAssembly代码。


环境安装及简介

1. Rust

一门赋予每个人
构建可靠且高效软件能力的语言。

安装

# macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 其他安装方式
# https://forge.rust-lang.org/infra/other-installation-methods.html
Enter fullscreen mode Exit fullscreen mode

常用命令

# 版本更新
rustup update

# 查看版本
cargo --version

# 构建项目
cargo build

# 运行项目
cargo run

# 测试项目
cargo test

# 为项目构建文档
cargo doc

# 将库发布到 crates.io
cargo publish
Enter fullscreen mode Exit fullscreen mode
# nightly rust
rustup toolchain install nightly

rustup toolchain list

rustup override set nightly
Enter fullscreen mode Exit fullscreen mode

2. Node.js

Node.js是基于Chrome的V8 JavaScript引擎构建的JavaScript运行时

3. wasm-pack

用于构建和使用您希望与JavaScript,浏览器或Node.js互操作的Rust生成的WebAssembly。

安装

# macOS
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# 其他安装方式
# https://rustwasm.github.io/wasm-pack/installer
Enter fullscreen mode Exit fullscreen mode

常用命令

# 创建
# https://rustwasm.github.io/docs/wasm-pack/commands/new.html
wasm-pack new <name> --template <template> --mode <normal|noinstall|force>

# 构建
# https://rustwasm.github.io/docs/wasm-pack/commands/build.html
wasm-pack build
  [--out-dir <out>]
  [--out-name <name>]
  [--<dev|profiling|release>]
  [--target <bundler|nodejs|web|no-modules>]
  [--scope <scope>]
  [mode <normal|no-install>]

# 测试
# https://rustwasm.github.io/docs/wasm-pack/commands/test.html
wasm-pack test

# 发包
# https://rustwasm.github.io/docs/wasm-pack/commands/pack-and-publish.html
# npm pack
wasm-pack pack
# npm publish
wasm-pack publish
Enter fullscreen mode Exit fullscreen mode

4. Vite

下一代前端工具

vite-plugin-rsw:vite插件,简称Rsw - 集成wasm-pack的CLI

  • 支持rust包文件热更新,监听src目录和Cargo.toml文件变更,自动构建
  • vite启动优化,如果之前构建过,再次启动npm run dev,则会跳过wasm-pack构建
  • 通过配置isLibtrue,在执行npm run build时会生成可发布的npm包
# 在vite项目中安装
npm i -D vite-plugin-rsw
# or
yarn add -D vite-plugin-rsw
Enter fullscreen mode Exit fullscreen mode

5. create-xc-app

脚手架 - ⚡️在几秒钟内创建一个项目!维护了多种项目模板。

# 根据命令行提示,输入项目名称,选择模板初始化项目
# template: `wasm-react` or `wasm-vue`
npm init xc-app
Enter fullscreen mode Exit fullscreen mode

xc-app

快速开始

  • 在原有vite项目中使用,只需安装配置vite-plugin-rsw插件即可。
  • 新项目可以使用vite提供的@vitejs/app初始化项目,然后安装配置vite-plugin-rsw
  • 或者使用脚手架create-xc-app初始化项目,模板包含wasm-reactwasm-vue,会定期更新维护相关版本依赖。

项目结构

# 推荐目录结构
[my-wasm-app] # 项目根路径
|- [wasm-hey] # npm包`wasm-hey`
|    |- [pkg] # 生成wasm包的目录
|    |    |- wasm-hey_bg.wasm # wasm文件
|    |    |- wasm-hey.js # 包入口文件
|    |    |- wasm-hey_bg.wasm.d.ts # ts声明文件
|    |    |- wasm-hey.d.ts # ts声明文件
|    |    |- package.json
|    |    `- ...
|    |- [src] # rust源代码
|    | # 了解更多: https://doc.rust-lang.org/cargo/reference/cargo-targets.html
|    |- [target] # 项目依赖,类似于npm的`node_modules`
|    | # 了解更多: https://doc.rust-lang.org/cargo/reference/manifest.html
|    |- Cargo.toml # rust包管理清单
|    `- ...
|- [@rsw] # npm 组织包
|     |- [hey] # @rsw/hey, 目录结构同`wasm-hey`
|     `- ...
| # 设置`isLib`为true时,会默认在`libs`目录下生成的npm包
| # 可以通过设置`libRoot`修改默认路径
|- [libs]
|     |- [@rsw]
|     `- [wasm-hey]
|- [node_modules] # 前端的项目包依赖
|- [src] # 前端源代码(可以是vue, react, 或其他)
| # 了解更多: https://nodejs.dev/learn/the-package-json-guide
|- package.json # `npm`或`yarn`包管理清单
| # 了解更多: https://vitejs.dev/config
|- vite.config.ts # vite配置文件
| # 了解更多: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
|- tsconfig.json # typescript配置文件
` ...
Enter fullscreen mode Exit fullscreen mode

乍一看,可能会觉得目录有点复杂,其实它就是一个标准的基于vite前端项目,然后,在根路径下去添加我们需要构建的wasm包(一个rust crate会对应生成一个wasm包,可单独发布到npm上)

创建Wasm包

# 两种方式创建

# 1.
# 如果报错,可查看:https://github.com/rustwasm/wasm-pack/issues/907
wasm-pack new <name>

# 2.
# name可以是npm组织
# 例:cargo new --lib @rsw/hello
# 需要手动配置Cargo.toml
cargo new --lib <name>
Enter fullscreen mode Exit fullscreen mode

wasm-pack new
cargo new

项目配置

以react项目为例

Step1: 配置Vite插件 - vite.config.ts

import reactRefresh from '@vitejs/plugin-react-refresh';
import { defineConfig } from 'vite';
import ViteRsw from 'vite-plugin-rsw';

export default defineConfig({
  plugins: [
    reactRefresh(),
    // 查看更多:https://github.com/lencx/vite-plugin-rsw
    ViteRsw({
      // 支持开发(dev)和生产模式(release)
      // 生产模式会对wasm文件的体积进行优化
      mode: 'release',
      // 是否生成可发布的npm包,默认为`false`
      // 如果设置为`true`时,会在项目根路径下生成`libs`目录
      isLib: true,
      // 修改默认路径`libs`
      libRoot: 'libs',
      // 如果包在`unLinks`和`crates`都配置过
      // 会执行,先卸载(npm unlink),再安装(npm link)
      // 例如下面会执行
      // `npm unlink wasm-hey rsw-test`
      unLinks: ['wasm-hey', 'rsw-test'],
      // 项目根路径下的rust项目
      // `@`开头的为npm组织
      // 例如下面会执行:
      // `npm link wasm-hey @rsw/hey`
      // 因为执行顺序原因,虽然上面的unLinks会把`wasm-hey`卸载
      // 但是这里会重新进行安装
      crates: ["wasm-hey", "@rsw/hey"],
    }),
  ],
})
Enter fullscreen mode Exit fullscreen mode

Step2: 配置Rust项目清单 - wasm-hey/Cargo.toml

# ...

# https://github.com/rustwasm/wasm-pack/issues/886
# https://developers.google.com/web/updates/2019/02/hotpath-with-wasm
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]

[profile.release]
opt-level = "s"

[dependencies]
wasm-bindgen = "0.2.70"
Enter fullscreen mode Exit fullscreen mode

Step3: 添加Rust代码 - wasm-hey/src/lib.rs

use wasm_bindgen::prelude::*;

// Import the `window.alert` function from the Web.
#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

// Export a `greet` function from Rust to JavaScript, that alerts a hello message.
#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}
Enter fullscreen mode Exit fullscreen mode

Step4: React项目中调用Wasm方法 - src/App.tsx

import React, { useEffect } from 'react';
import init, { greet } from 'wasm-hey';

import logo from './logo.svg';
import './App.css';

function App() {
  useEffect(() => {
    // wasm初始化,在调用`wasm-hey`包方法时
    // 必须先保证已经进行过初始化,否则会报错
    // 如果存在多个wasm包,则必须对每一个wasm包进行初始化
    init();
  }, [])

  const handleHey = () => {
    // 调用greet方法
    greet('wasm');
  }

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello WebAssembly!</p>
        <p>Vite + Rust + React</p>
        <p>
          <button onClick={handleHey}>hi wasm</button>
        </p>
        <p>Edit <code>App.tsx</code> and save to test HMR updates.</p>
      </header>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

常见问题汇总

Rsw插件

  • 插件内部是通过npm link的形式实现的wasm包安装,在一些极端场景下会出现,找不到依赖的安装包,导入的包不存在等错误,可以根据提示路径删除其link的文件,重新启动npm run dev可以解决。
  • npm link命令会把包link到全局环境,如果在多个项目使用相同wasm包名,可能会导致报错,解决办法,在全局npm的node_modules中删除该包即可。推荐不同项目使用不同wasm包名避免此类异常。
  • 插件是处于Vite开发模式下运行构建,所以至少执行过一次npm run dev,生成wasm包之后,再执行npm run build,否则也会报错,到不到.wasm文件之类的。
  • 插件API可以配置需要卸载的包(仅限于之前通过插件配置crates中rust项目)

前端

// init是wasm实例的初始化方法
// 在调用其他方法之前,必须先调用一次init方法,否则会报错
// init会请求`.wasm`文件并且返回一个`Promise`
import init, { greet } from 'wasm-test';

// -----------------------------------------

// 调用init方法,有两种方式

// 1.
// 在react,vue3中可以将其抽离为`hook`组件,
// 在进入生命周期时调用
init();

// 在调用过init方法之后,可以单独调用greet方法
greet('wasm');

// 2.
// 在初始化之后直接调用方法
init()
  .then(wasm => wasm.greet('wasm'));
Enter fullscreen mode Exit fullscreen mode

相关链接

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (1)

Collapse
 
scathonlin profile image
ScathonLin

中国同胞来捧个场哈哈

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay