Setup de WebAssembly
Antes de fazermos o setup de WebAssembly para nosso computador, vamos entender o porquê de WebAssembly e o que é YewStack que vamos utilizar.
WebAssembly
O que é WebAssembly
WebAssembly, também conhecido como Wasm
, é um padrão que define um formato binário portável e compacto para executáveis com velocidades quase nativas. Sua correspondente textual da linguagem assembly é o .wat
, que utiliza expressões-s
, que lembra um pouco Clojure e Scheme. Sua principal aplicação é como interface simples entre o programa que você desenvolveu e seu host ambiente, assim seu maior objetivo é possibilitar páginas web de alta performance, mas existem outros exemplos aplicando os conceitos de WemAssemlby para mobile por exemplo.
Por que WebAssembly
Aplicações web JavaScript possui uma certa incerteza quanto a performance que o produto final vai atingir. Seu sistema de tipos dinâmico dificulta muitas otimizações e o Garbage Collector muitas vezes não ajuda por depender de algumas pausas confusas. Além disso, aplicar um superset tipado não garante efetivamente uma melhora nesses quesitos, pois no final das contas, continua tendo que executar JavaScript. Outro grande problema pode ser o fato de pequenas mudanças impactarem profundamente a performance caso você caia em buracos como fadiga do JavaScript
(JavaScript fatigue) e como inferno de dependências
(dependency hell), que podem confundir muito o sistema de JIT. Outro ponto importante é o tamanho do código, especialmente quando falamos de dependency hell, o JavaScript pode causar grande impacto de performance, pois o tamanho do arquivo que vamos comunicar via internet é crucial.
Assim, Rust provê um controler de baixo nível e uma performance confiável, especialmente por não depender de Garbage Collectors não deterministicos como o JavaScript, por possibilitar arquivos de tamanho reduzido, já que você só recebe o que você precisa, e por dar poder as pessoas que vão desenvolver de como o layout de memória será. Outra vantagem é que WebAssembly permite uma portabilidade muito boa, de forma que você pode começar implementando apenas as partes mais críticas de seu JavaScript.
YewStack
Yew é um framework moderno para criar apps frontend multi-threaded com WebAssembly e Rust.
- Possui um framework baseado em componentes que permite criar UIs de forma interativa. Assim, quem já possui experiência de React e Elm devem se sentir bastante confortáveis com o funcionamento do Yew.
- Sua performance se da por minimizar chamadas a API de DOM e por permitir muito processamento nos web workers de background.
- Permite ótima interoperabilidade com JavaScript, o que permite desenvolver aproveitando os pacotes NPM e integrar com aplicações JavaScript já existentes.
Setup
Primeiro devemos começar com o setup do WebAssembly para Rust:
- Começamos com o
wasm-pack
, uma ferramenta para construir, testar e publicar WebAssembly gerado por Rust. Basta executar o comandocurl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
ou acessar o site https://rustwasm.github.io/wasm-pack/installer/ para mais informações. Outra opção é utilizar ocargo install wasm-pack
. - O segundo passo é instalar o
cargo-generate
, que pode ser feito através do comandocargo install cargo-generate
. Sua função é facilitar a subida e execução de novos projetos e seus tempaltes em Rust. - Por fim, instalar o NPM de sua escolha, uma sugestão é fazer o download pelo site https://www.npmjs.com/get-npm e executar
npm install npm@latest -g
.
Setup para o Yew
Para utilizarmos o Yew vamos precisar de 2 novos pacotes, wasm-bindgen
e cargo-web
, e uma dependência NPM chamada rollup
.
wasm-bindgen
pode ser encontrado comodependencies
.- Para instalar o
cargo-web
você precisa executar o comandocargo install cargo-web
. Para obuild
basta utilizarcargo web build
e orun
utilizarcargo web run
. Necessário parastdweb
. - Para o exemplo teste precisamos instalar o
rollup
executenpm install --global rollup
- Para o nosso exemplo de teste vamos utilozar Python, ou Python3 se você preferir, e o módulo
http
isntalado compip install http
. Outros servidores também seriam possíveis como ominiserve
do cargo, que pode ser obtido através do comandocargo +nightly install miniserve
.
Os alvos suportados são:
wasm32-unknown-unknown
wasm32-unknown-emscripten
asmjs-unknown-emscripten
Hello World
Nesse exemplo vamos utilizar o template mínimo da yew-stack
, para isso faça clone do repositório https://github.com/yewstack/yew-wasm-pack-minimal.git. Você verá que o arquivo Cargo.toml
está configurado da seguitne forma:
[package]
authors = [
"Kelly Thomas Kline <kellytk@sw-e.org>",
"Samuel Rounce <me@samuelrounce.co.uk>"
]
categories = ["gui", "wasm", "web-programming"]
description = "yew-wasm-pack-minimal demonstrates the minimum code and tooling necessary for a frontend web app with simple deployable artifacts consisting of one HTML file, one JavaScript file, and one WebAssembly file, using Yew, wasm-bindgen, and wasm-pack."
edition = "2018"
keywords = ["yew", "wasm", "wasm-bindgen", "web"]
license = "MIT/Apache-2.0"
name = "yew-wasm-pack-minimal"
readme = "README.md"
repository = "https://github.com/yewstack/yew-wasm-pack-minimal"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "^0.2"
yew = "0.12"
Na tag author
mude para seu nome e seu email, a description
pode ser "hello world in wasm"
, a tag name
pode ser hello-world-wasm
, estaremos utilizando websys
. Caso você deseje utilizar a stdweb
basta modificar a dependência yew
para yew = { version = "0.15", package = "yew-stdweb" }
. Alguns exemplos da diferença entre websys
e stdweb
:
#![allow(unused)] fn main() { // web-sys let window: web_sys::Window = web_sys::window().expect("window not available"); window.alert_with_message("hello from wasm!").expect("alert failed"); // stdweb let window: stdweb::web::Window = stdweb::web::window(); window.alert("hello from wasm!"); // stdweb com a macro js! use stdweb::js; use stdweb::unstable::TryFrom; use stdweb::web::Window; let window_val: stdweb::Value = js!{ return window; }; let window = Window::try_from(window_val).expect("conversion to window failed"); window.alert("hello from wasm!"); }
Dentro da macro js!
é possível utilizar JavaScript, como no exemplo js!{ return window; }
.
Diferenças entre web-sys
e stdweb
| | web-sys | stdweb |
|- |- |- |
| Status do projeto | Ativamente mantido pelo wasm-working-group | Pouca atividade no Github, já passou longos períodos sem commits |
| Cobertura da Web API | APIs Rust são auto geradas pela spec Web IDL e deveriam ter cobertura de ~100%. | APIs do Browser são adicionadas de acordo com as necessidades da comunidade |
| Design da API Rust | Toma medidas conservadoras ao retornar quase sempre um Result
para as chamadas da API | Prefere utilizar panic!
em vez de Result
. |
| Suporte para build tools | * wasm-pack
* wasm-bindgen
| * wasm-pack
* wasm-bindgen
* cargo-web
|
| Alvos suportados | * wasm32-unknown-unknown
| * wasm32-unknown-unknown
* wasm32-unknown-emscripten
* asmjs-unknown-emscripten
|
Executando o projeto
Opção 1
- Para buildar o projeto execute
wasm-pack build --target web
. - Para fazer o bundle utilize
rollup ./main.js --format iife --file ./pkg/bundle.js
. - Para expor seu bundle no browser execute o comando
python -m SimpleHTTPServer
para Python2 epython3 -m http.server
para Python3, depois acesse em seu browserlocalhost:8000
para ver um bome velhoHello world!
.
Opção 2
- Crie a pasta
static
através de ummkdir static
e inclua o seguinte arquivoindex.html
:
<html lang="en">
<head>
<meta charset="utf-8">
<title>Yew Sample App</title>
<script type="module">
import init from "./wasm.js"
init()
</script>
</head>
<body></body>
</html>
- Modifique a
src/lib.rs
para expor a função:
#![allow(unused)] fn main() { mod app; use wasm_bindgen::prelude::*; use yew::App; #[wasm_bindgen(start)] pub fn run_app() { App::<app::App>::new().mount_to_body(); } }
- Execute o comando
wasm-pack build --target web --out-name wasm --out-dir ./static
para buildar seu projeto. - Disponibilize o bundle no browser com
miniserve ./static --index index.html
. E pronto, basta acessarlocalhost:8080
.
Ao longo das próximas etapas do projeto vamos utilizar a opção 2
, mas se você preferir utilizar a opção 1
fique a vontade.