最速でWeb Assemblyを使ったweb pageを試す
動かし方
1. 適当なdirectoryで↓を実行する
要wasm-pack
code:sh
deno run --unstable --allow-net --allow-read --allow-write --allow-run https://scrapbox.io/api/code/deno-ja/最速でWeb_Assemblyを使ったweb_pageを試す/build.ts
2. 同じdirectoryで↓を実行する
code:sh
deno run --allow-net --allow-read https://scrapbox.io/api/code/deno-ja/最速でWeb_Assemblyを使ったweb_pageを試す/script.tsx
3. 出力されたURLを押す
http serverを立てる
code:script.tsx
// @deno-types="https://deno.land/x/servest@v1.3.1/types/react/index.d.ts"
import React from 'https://esm.sh/react@17.0.2';
// @deno-types="https://deno.land/x/servest@v1.3.1/types/react-dom/server/index.d.ts"
import ReactDOMServer from 'https://esm.sh/react-dom@17.0.2/server';
import { createApp } from "https://deno.land/x/servest@v1.3.1/mod.ts";
import { lookup, contentType } from "https://deno.land/x/media_types@v2.8.2/mod.ts";
const app = createApp();
// メインのHTMLファイルを返す
app.handle("/", async (req) => {
const res = await fetch('https://scrapbox.io/api/code/deno-ja/最速でWeb_Assemblyを使ったweb_pageを試す/index.html');
await req.respond({
status: 200,
headers: new Headers({
"content-type": "text/html; charset=UTF-8",
}),
body: ReactDOMServer.renderToString(
<html>
<head>
<meta charSet="utf-8" />
<title>Web page demo</title>
<script type="module" src="./index.js" />
</head>
<body></body>
</html>
),
});
});
// index.jsを返す
app.get("/index.js", async (req) => {
await req.respond({
status: 200,
headers: new Headers({
"content-type": "application/javascript; charset=UTF-8",
}),
body: await Deno.readTextFile('index.js'),
});
});
// pkg/にあるファイルを返す
app.get(/^\/pkg/, async (req) => {
await req.respond({
status: 200,
headers: new Headers({
"content-type": contentType( lookup(.${req.path}) ?? "text/plain") ?? "text/plain; charset=UTF-8",
}),
body: await Deno.readTextFile(.${req.path}),
});
});
const port = 8888;
app.listen({ port });
console.log(go to http://localhost:${8888});
from https://github.com/rustwasm/wasm-bindgen/blob/master/examples/without-a-bundler/index.html
改変
code:index.js
import {wasm} from './pkg/web_page_demo_bg.wasm.js';
import init, { add } from './pkg/web_page_demo.js';
(async () => {
await init(wasm); // wasmを読み込む
// 読み込んだ函数を使う
const result = add(1, 2);
console.log(1 + 2 = ${result});
if (result !== 3) throw new Error("wasm addition doesn't work!");
})();
RustのcodeをWeb Assemblyにするbuild script
予めwasmをarray bufferに変換しておかないと、CompileError: wasm validation errorが出てしまう
https://github.com/hazae41/denoflate/blob/master/build.ts の手法を使った
code:build.ts
import { exec, OutputMode } from "https://deno.land/x/exec@0.0.5/mod.ts";
import { ensureFile } from "https://deno.land/std@0.95.0/fs/mod.ts";
const entry = 'https://scrapbox.io/api/code/deno-ja/最速でWeb_Assemblyを使ったweb_pageを試す';
// 必要なファイルをdownloadする
{
const filename = 'lib.rs';
await ensureFile(./src/${filename});
const res = await fetch(${entry}/${filename});
await Deno.writeTextFile(./src/${filename}, await res.text());
}
{
const filename = 'Cargo.toml';
await ensureFile(filename);
const res = await fetch(${entry}/${filename});
await Deno.writeTextFile(filename, await res.text());
}
{
const filename = 'index.js';
await ensureFile(filename);
const res = await fetch(${entry}/${filename});
await Deno.writeTextFile(filename, await res.text());
}
// compileを実行する
await exec('wasm-pack build --target web --release', {output: OutputMode.Capture, verbose: true});
// 予めUint8Arrayにしておく
const wasm = await Deno.readFile("./pkg/web_page_demo_bg.wasm");
await Deno.writeTextFile("./pkg/web_page_demo_bg.wasm.js",
export const wasm = new Uint8Array([${wasm.join(",")}]););
Web AssemblyにするRust code
from https://github.com/rustwasm/wasm-bindgen/blob/master/examples/without-a-bundler/src/lib.rs
code:lib.rs
use wasm_bindgen::prelude::*;
// Called when the wasm module is instantiated
#wasm_bindgen(start)
pub fn main() -> Result<(), JsValue> {
// Use web_sys's global window function to get a handle on the global
// window object.
let window = web_sys::window().expect("no global window exists");
let document = window.document().expect("should have a document on window");
let body = document.body().expect("document should have a body");
// Manufacture the element we're gonna append
let val = document.create_element("p")?;
val.set_inner_html("Hello from Rust!");
body.append_child(&val)?;
Ok(())
}
#wasm_bindgen
pub fn add(a: u32, b: u32) -> u32 {
a + b
}
Rust用project setting
from https://github.com/rustwasm/wasm-bindgen/blob/master/examples/without-a-bundler/Cargo.toml
改変してある
code:Cargo.toml
package
name = "web-page-demo"
version = "0.1.0"
authors = "The wasm-bindgen Developers", "takker"
edition = "2021"
lib
crate-type = "cdylib"
dependencies
wasm-bindgen = "0.2.73"
dependencies.web-sys
version = "0.3.4"
features = [
'Document',
'Element',
'HtmlElement',
'Node',
'Window',
]