We all know that Rust has a project called napi for developing npm libraries on the Node.js platform. Essentially, napi is a binding to Node.js’s C-API FFI. However, libraries developed with napi cannot run directly in pure browser environments because napi is Node.js’s native addon API and relies on Node.js’s runtime environment - which browsers don’t have.
For pure browser environments, we need to use WASM to implement browser-compatible functionality.
Fortunately, Rust is also a hot topic in the WASM field with a mature ecosystem. We can use wasm-pack to simplify our development experience.
Installation and Usage
You can install it using any of these four methods:
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh # Method 1
cargo install wasm-pack # Method 2
npm install -g wasm-pack # Method 3
pnpm add -g wasm-pack # Method 4
Let’s take implementing PNG-to-JPEG conversion as an example. Create a project:
wasm-pack new imagejs
Open the project, and you’ll find sample code already in lib.rs.
wasm-bindgen
The #[wasm_bindgen] attribute indicates that the function below it can be accessed in both JavaScript and Rust.
There’s a code example:
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
This extern block imports the JavaScript alert function into Rust. This declaration is required to call alert from Rust. By declaring it this way, wasm-bindgen will create JavaScript stubs for alert, allowing us to pass strings between Rust and JavaScript.
Now we can directly call the JavaScript alert function in Rust code. Note that any function calling alert must also be declared with the #[wasm_bindgen] attribute.
For the PNG-to-JPEG conversion feature, our function signature could look like this:
#[wasm_bindgen]
pub fn png_to_jpg(buf: &[u8]) -> Result<Vec<u8>, JsError> {
todo!()
}
This function receives PNG byte data and returns converted JPEG byte data.
For image format conversion, we can use the image crate:
cargo add image
Important: Since we’re targeting WASM, not all Rust crates support WASM environments. Always verify WASM compatibility before using a crate.
The image crate is straightforward to use:
#[wasm_bindgen]
pub fn png_to_jpg(buf: &[u8]) -> Result<Vec<u8>, JsError> {
let format = image::ImageFormat::Png;
let img = image::load_from_memory_with_format(buf, format)?;
let mut bytes: Vec<u8> = Vec::new();
img.write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Jpeg)?;
Ok(bytes)
}
Run the build command:
wasm-pack build --target web
This generates a browser-compatible npm package with the following structure:
pkg
├── imagejs_bg.wasm
├── imagejs_bg.wasm.d.ts
├── imagejs.d.ts
├── imagejs.js
├── package.json
└── README.md
Frontend Validation
Create a standard Vue project for validation:
pnpm create vue@latest
Two important points to note:
1. Import local package
{
"dependencies": {
"imagejs": "file:../pkg",
"vue": "^3.5.13"
}
}
Add the file path and run pnpm install.
2. Configure Vite for WASM
Use vite-plugin-wasm:
pnpm add -g vite-plugin-wasm
Configure vite.config.ts:
import wasm from "vite-plugin-wasm";
export default defineConfig({
plugins: [
wasm(),
vue(),
vueDevTools(),
],
optimizeDeps:{
exclude: ['imagejs']
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})
Feature Validation
Initialize before use:
import init, {png_to_jpg} from 'imagejs'
onMounted(async () => {
await init()
})
Create a form for PNG upload, conversion to JPEG, and browser display:
<template>
<div>
<h1>convert png to jpeg</h1>
<div>
<input type="file" @change="changeHandle">
<img :src="showUrl" alt=""></img>
</div>
</div>
</template>
Implementation logic:
// Reactive variable for displaying converted image URL
const showUrl = ref('')
async function changeHandle(e: Event) {
let target: HTMLInputElement = e.target as HTMLInputElement
if (target.files) {
let file = target.files[0]
let buf = await file.arrayBuffer()
let buf2 = png_to_jpg(new Uint8Array(buf))
let url = URL.createObjectURL(new Blob([buf2], { type: 'image/jpeg' }))
showUrl.value = url
}
}
Publishing to npm Registry
Publish using wasm-pack publish (requires npm account and login):
wasm-pack login