By Chen Xin (Lencx)/Edited by Zhang Handong

What is Wasm?

The official MDN document gives the definition as follows

WebAssembly(Wasm for writing purposes) is a new way of coding that can be run in modern Web browsers – it is a low-level assembler like language with a compact binary format that runs close to native performance and provides a compilation target for languages such as C/C ++, So that they can run on the Web. It is also designed to coexist with JavaScript, allowing the two to work together.

WebAssembly is huge for Web platforms — it provides a way for code written in a variety of languages to run on the Web at near-native speeds. In this case, client software that could not run this way before will be able to run on the Web.

WebAssembly is designed to work with JavaScript — by using WebAssembly’s JavaScript API, you can load WebAssembly modules into a JavaScript application and share functionality between the two. This allows you to leverage the performance and power of WebAssembly and the expressive power and flexibility of JavaScript in the same application, even though you may not know how to write WebAssembly code.


Environment installation and introduction

1. Rust

A language that empowers everyone to build reliable and efficient software.

The installation

# macOS
curl --proto '=https'- tlsv1.2 - sSf https://sh.rustup.rs | sh# Other installation methods
# https://forge.rust-lang.org/infra/other-installation-methods.html
Copy the code

Common commands

# Version update
rustup update

# check version
cargo --version

# Build the project
cargo build

# Run the project
cargo run

# Test items
cargo test

Build documentation for the project
cargo doc

Publish libraries to crates. IO
cargo publish
Copy the code
# nightly rust
rustup toolchain install nightly

rustup toolchain list

rustup override set nightly
Copy the code

2. Node.js

Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine

3. wasm-pack

For building and using Rust-generated WebAssemblies that you want to interoperate with JavaScript, browsers, or Node.js.

The installation

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

# Other installation methods
# https://rustwasm.github.io/wasm-pack/installer
Copy the code

Common commands

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

# building
# 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>]

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

# contract
# https://rustwasm.github.io/docs/wasm-pack/commands/pack-and-publish.html
# npm pack
wasm-pack pack
# npm publish
wasm-pack publish
Copy the code

4. Vite

Next generation front-end tools

vite-plugin-rsw: Vite plug-in for shortRsw– integrationwasm-packThe CLI

  • Support hot update of RUST package files and monitorsrcDirectory andCargo.tomlFile changes, automatic build
  • Vite starts optimization and starts again if it was built beforenpm run dev, will be skippedwasm-packbuild
Install in the Vite project
npm i -D vite-plugin-rsw
# or
yarn add -D vite-plugin-rsw
Copy the code

5. create-xc-app

Scaffolding – ⚡️ Create a project in seconds! Maintained a variety of project templates.

Follow the command line prompt, enter the project name, select template initialization project
# template: `wasm-react` or `wasm-vue`
npm init xc-app
Copy the code

Quick start

  • In the originalviteFor use in projects, just install the configurationvite-plugin-rswPlugins.
  • New items can be usedviteTo provide the@vitejs/appInitialize the project, and then install the configurationvite-plugin-rsw.
  • Or use scaffoldingcreate-xc-appInitialize the project, template containswasm-reactandwasm-vue, will periodically update and maintain the related version dependency.

The project structure

# Recommend directory structure
[my-wasm-app] # project root path
|- [wasm-hey] # NPM package ` wasm - hey `
|    |- [pkg] Create wASM package directory
|    |    |- wasm-hey_bg.wasm # wasm file
|    |    |- wasm-hey.js Package entry file
|    |    |- wasm-hey_bg.wasm.d.ts # ts declaration file
|    |    |- wasm-hey.d.ts # ts declaration file
|    |    |- package.json
|    |    `- ...
|    |- [src] # Rust source code
|    | # learn more: https://doc.rust-lang.org/cargo/reference/cargo-targets.html
|    |- [target] # project dependencies, similar to NPM's 'node_modules'
|    | # learn more: https://doc.rust-lang.org/cargo/reference/manifest.html
|    |- Cargo.toml # Rust package Management manifest
|    `- ...
|- [@rsw] # NPM organization package
|     |- [hey] # @rsw/hey, same directory structure as' wASM-hey '
|     `- ...
|- [node_modules] # Front-end project package dependencies
|- [src] # Front-end source code (can be vue, React, or others)
| # learn more: https://nodejs.dev/learn/the-package-json-guide
|- package.json # 'NPM' or 'YARN' package management list
| # learn more: https://vitejs.dev/config
|- vite.config.ts # vite configuration file
| # learn more: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
|- tsconfig.json # typescript configuration files`...Copy the code

At first glance, the directory may seem a bit complicated, but it is a standard Vite based front-end project. Then, add the WASM package we need to build to the root path (a Rust Crate generates a WASM package, which can be published separately to NPM).

Create Wasm package

# Two ways to create

# 1.
# if an error, can look at: https://github.com/rustwasm/wasm-pack/issues/907
wasm-pack new <name>

# 2.
# name can be an NPM organization
Cargo new --lib @rsw/hello
Cargo. Toml needs to be manually configured
cargo new --lib <name>
Copy the code

Project configuration

Take the React project for example

Step1: Configure the Vite plug-in – 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(),
    / / read more: https://github.com/lencx/vite-plugin-rsw
    ViteRsw({
      // Support development (dev) and production (release)
      // Production mode optimizes the size of wASM files
      mode: "release".If the package is configured in both 'unLinks' and' crates'
      NPM unlink (NPM unlink);
      // For example
      // `npm unlink wasm-hey rsw-test`
      unLinks: ['wasm-hey'.'rsw-test'].// The rust project in the project root path
      // '@' starts with NPM organization
      // For example:
      // `npm link wasm-hey @rsw/hey`
      // For execution order reasons, although the above unLinks will unmount 'wASM-hey'
      // But the installation will be redone
      crates: ["wasm-hey"."@rsw/hey"],}),],})Copy the code

Step2: Configure the Rust project list – wASM-hey /Cargo

#...

# 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"
Copy the code

Step3: Add Rust code -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));
}
Copy the code

Step4: Invoke the Wasm method -src/app.tsx in the React project

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

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

function App() {
  useEffect(() = > {
    // WASM initialization, when the 'wASM-hey' package method is called
    // Ensure that the initialization has been performed, otherwise an error will be reported
    // If there are multiple WASM packages, each wASM package must be initialized
    init();
  }, [])

  const handleHey = () = > {
    // Call the greet method
    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
Copy the code

FAQ Summary

Rsw plug-in

  • The plugin is internal throughnpm linkWasm package installation is implemented in the form of wASM package installation, in some extreme scenarios will appear, can not find the dependent installation package, the imported package does not exist and other errors, you can delete its link file according to the prompt path, restartnpm run devIt can be solved.
  • npm linkThe command will put the packagelinkTo the global environment, if the same WASM package name is used in multiple projects, it may cause an error to be reported, resolved in the global NPMnode_modulesTo delete the package. You are advised to use different WASM package names for different projects to avoid such exceptions.
  • The plug-in runs the build in Vite development mode, so it is executed at least oncenpm run devTo generate thewasmAfter the package is executednpm run buildOtherwise, an error will be reported.wasmDocuments or something.
  • The plug-in API can configure packages that need to be uninstalled (only previously configured through the plug-in)cratesThe RUST Project)

The front end

// init is the wASM instance initialization method
Init must be called once before calling other methods, otherwise an error will be reported
// init will request '. Wasm 'files and return a' Promise '
import init, { greet } from 'wasm-test';

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

There are two ways to call the init method

/ / 1.
// React, vue3 can be separated into 'hook' components,
// called when the lifecycle is entered
init();

// Call the greet method alone after calling the init method
greet('wasm');

/ / 2.
// Call the method directly after initialization
init()
  .then(wasm= > wasm.greet('wasm'));
Copy the code

A link to the

  • Wasm learning project: Lencx/Learning-WASM
  • Vite plugin for rsv-lencx/vite-plugin-rsW
  • Project scaffolding – Lencx/CREate-XC-app
  • List of WebAssembly-related resources

  • WebAssembly website
  • Rust – a language that empowers everyone to build reliable and efficient software
  • Nodejs is a JavaScript runtime built on Chrome’s V8 JavaScript engine
  • Vite official website – the next generation of front-end tools
  • wasm-pack – Rust => WebAssembly
  • rust-to-wasm
  • wasm-bindgen

About the author:

Chen Xin (lencx)

Toss over confusing thinking]ing, on the road…

  • Public account: Floating static
  • Blog: mtc.nofwl.com
  • GitHub: github.com/lencx

Content: February issue of Rust Chinese (Rust_Magazine)