Using rust with vue
Using rust with vue
Recently I have been building wirt, a web app to help with easily configuring and managing Wireguard networks. One of the main features is the automatic generation of private and public keys right in the browser.
This is needed for privacy and security reasons. After all, who would want to trust anyone with their private keys? Especially some website on the internet that fetches them from an API.
So the keys needed to be created right in the browser and stay only on the users computer.
Enter rust
Wireguard uses Curve25519. For these kind of algorithms I did not want to put my trust in JavaScript but instead use a security focussed language, more specifically rust.
Give the rise of WebAssembly I set out to check how I can go about integrating it into my app.
And I have to say it has become a breeze!
What follows will be my setup as well as some tips on how to make the experience as painless as possible.
The setup
For my project I am using vue-cli, so this is what I will base this on.
The first thing you will need is rust. It can be easliy installed using rustup.
With that out of the way go to your project and install npm install --save-dev wasm-tool/wasm-pack-plugin
.
It will take care of all the difficult rust compilation steps for you.
Then update the webpack bundling configuration to include the .wasm
files into the bundle.
Create vue.config.js
if you dont have one yet and add the following:
const path = require("path");
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
const webpack = require("webpack");
module.exports = {
chainWebpack: (config) => {
// rust wasm bindgen https://github.com/rustwasm/wasm-bindgen
config
.plugin("wasm-pack")
.use(WasmPackPlugin)
.init(
(Plugin) =>
new Plugin({
crateDirectory: path.resolve(__dirname, "./wasm"),
})
)
.end()
// needed for Edge browser https://rustwasm.github.io/docs/wasm-bindgen/examples/hello-world.html
.plugin("text-encoder")
.use(webpack.ProvidePlugin)
.init(
(Plugin) =>
new Plugin({
TextDecoder: ["text-encoding", "TextDecoder"],
TextEncoder: ["text-encoding", "TextEncoder"],
})
)
.end();
},
};
Et Voila. Thats it for the setup.
Hello world
In the webpack config ./wasm
is specified as the location for the wasm code (check crateDirectory
).
I am not going to bore you with my code here, simply check out this great template to get a good Hello, World!
example.
Just copy the rust code from that template into a wasm
folder in your projects root directory. You will need the src
directory and the Cargo.toml
file (make sure to update the name and author!).
Now its time to call the rust code from JavaScript.
For this post I am using a blank vue project created with npx @vue/cli create
which provides an App.vue
component.
This is the slightly updated one with wasm support:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
<button @click="greet">Greet me</button>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
components: {
HelloWorld,
},
methods: {
async greet() {
const wasm = import("../wasm/pkg");
const greet = (await wasm).greet;
greet();
},
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
See how greet()
just calls out to wasm? It really is that simple! wasm-pack and wasm-bindgen will do all the heavy lifting for you.
Thats it
Its done. These few steps are all that needed to get started implementing rust logic into your vue websites and apps.
But there is much more to discover. I highly recommend reading the documentations at https://rustwasm.github.io/docs/wasm-bindgen/ and https://rustwasm.github.io/docs/wasm-pack/. This will help you to optimize your bundles and learn about all the cool things youll be able to do in the browser with amazing performance and about some problems that are still around.
And I promised some tips, so here they are:
- make sure that your libraries do not use threads! This wont work
- Same goes for 64bit integers. Always enable features that fall back to 32bit
Hopefully this will save you some headaches.
To round things off here is a skeleton application that follows the above steps so you dont have to: https://github.com/b-m-f/vue-rust-skeleton .
Ill go back to improve https://wirtbot.com now. Happy coding!
Cover Photo by Jason Leung on Unsplash