This project is just a playground to show how to leverage WASM
in Vue.js
applications.
It was generated using the Vue CLI 3.0
by using vue ui
and selecting a default TypeScript
project.
You can see the associated slides here.
$ npm i
$ npm run serve
Open http://localhost:8080
.
In order for Webpack
to be able to load .wasm
modules without having to specify the extension, the vue.config.js
had to be configured to allow such extension.
typings.d.ts
file was added to provide support for vue-slider-component
.
master
: Ready to start the implementation of the WASM modules.completed
: WASM module implemented and imported.completed-extern
: If you're curious about how to callJS
functions fromWASM
, checkout this branch.
Follow this link to learn how to install it.
This may take up to 3 minutes.
cargo install wasm-pack
Create a wasm
folder in the root and cd
into it:
mkdir wasm
cd wasm
Then we're going to create our library:
cargo new wasm-worker --lib
The previous instruction should have generated a wasm-worker
folder. Get to wasm > wasm-worker > src > Cargo.toml
and add the following:
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.27"
Go find wasm > wasm-worker > src > lib.rs
and type the following:
// importing the crate that will help us with the JS interaction.
extern crate wasm_bindgen;
// making it available to the current module
use wasm_bindgen::prelude::*;
// we're going to provide to functions similar to our jsLoop method
// but with two different implementations:
// the first one is a classic loop
// the second one uses 'fold' (reduce in JS)
// this will help us to see Rust Zero Cost Abstractions in action!
// this attribute is mandatory for functions that are going
// to be involved in communication process between JS and WASM.
#[wasm_bindgen]
pub fn calculate_loop(iterations: f64) -> f64 {
let iterations = iterations.round() as u64;
let mut result = 0;
for i in 0..iterations {
result += i;
}
result as f64
}
#[wasm_bindgen]
pub fn calculate_iter(iterations: f64) -> f64 {
let iterations = iterations.round() as u64;
(0..iterations).fold(0, |curr, acc| acc + curr) as f64
}
Given that we already have installed wasm-pack
, all we have to do is the following:
wasm-pack build
This will generate a pkg
folder inside the wasm > wasm-worker
folder.
Notice that wasm-pack
should have created our .wasm
file along with a package.json
, a .js
file containing some glue for the JS + WASM interaction, an a .d.ts
file.
First of all, open your src > App.vue
file.
We're going to import the typings from our WASM module:
import WasmWorker from '../wasm/wasm-worker/pkg';
// note that WASM modules have to be loaded asynchronously
// this is just to consume the typings
Insert this below the value
property declaration:
private wasmWorker!: typeof WasmWorker;
// note that we're using the previously imported module to get the typing
// later, we will assign the asynchronously loaded module to this variable
Let's create a constructor to load our WASM module:
constructor() {
super();
this.loadWasm();
}
And now let's implement the loadWasm
method, that must be asynchronous:
private async loadWasm() {
// we do load our WASM module here and assign it to our previously created variable
this.wasmWorker = await import('../wasm/wasm-worker/pkg');
// let's notify the user that everything is set up!
console.log(`%c WASM Loaded `, `background: #049741; color: #fff`);
}
You may have notice that we have two methods, namely wsLoop
and wsIter
, that have a comment stating that the method must be implemented. So, let's do that!
// wsLoop
return this.wasmWorker.calculate_loop(this.value);
// wsIter
return this.wasmWorker.calculate_iter(this.value);
Serve the app with npm run serve
and test that everything is working.
Let's change our Rust
library to consume some external JavaScript
functions.
Go find wasm > wasm-worker > src > lib.rs
and replace the previous content with the following:
// importing the crate that will help us with the JS interaction.
extern crate wasm_bindgen;
// making it available to the current module
use wasm_bindgen::prelude::*;
// we're making some JS methods available to our Rust functions.
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
#[wasm_bindgen(js_namespace = performance)]
fn now() -> f64;
#[wasm_bindgen(js_namespace = console)]
fn time(name: &str);
#[wasm_bindgen(js_namespace = console)]
fn timeEnd(name: &str);
}
// we're going to provide to functions similar to our jsLoop method
// but with two different implementations:
// the first one is a classic loop
// the second one uses 'fold' (reduce in JS)
// this will help us to see Rust Zero Cost Abstractions in action!
// this attribute is mandatory for functions that are going
// to be involved in communication process between JS and WASM.
#[wasm_bindgen]
pub fn calculate_loop(iterations: f64) -> f64 {
let title = "from wasm [loop]";
time(title);
let iterations = iterations.round() as u64;
let mut result = 0;
for i in 0..iterations {
result += i;
}
timeEnd(title);
result as f64
}
#[wasm_bindgen]
pub fn calculate_iter(iterations: f64) -> f64 {
let title = "from wasm [iter]";
time(title);
let iterations = iterations.round() as u64;
let result = (0..iterations).fold(0, |curr, acc| acc + curr) as f64;
timeEnd(title);
result
}
If you're not already logged in to the npm
registry, please do it. Then, from the wasm > wasm-worker
folder:
wasm-pack publish
This will publish our wasm-worker
package to npm
.
Now, proceed to src > App.vue
and change the path of the wasm-worker
imports.