Without a Bundler
This example shows how the --target web
flag can be used load code in a
browser directly. For this deployment strategy bundlers like Webpack are not
required. For more information on deployment see the dedicated
documentation.
First, you'll need to add web-sys
to your Cargo.toml.
[dependencies.web-sys]
version = "0.3.4"
features = [
'Document',
'Element',
'HtmlElement',
'Node',
'Window',
]
Then, let's take a look at the code and see how when we're using --target web
we're not actually losing any functionality!
use wasm_bindgen::prelude::*; // Called when the Wasm module is instantiated #[wasm_bindgen(start)] 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 }
Otherwise the rest of the deployment magic happens in index.html
:
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<!-- Note the usage of `type=module` here as this is an ES6 module -->
<script type="module">
// Use ES module import syntax to import functionality from the module
// that we have compiled.
//
// Note that the `default` import is an initialization function which
// will "boot" the module and make it ready to use. Currently browsers
// don't support natively imported WebAssembly as an ES module, but
// eventually the manual initialization won't be required!
import init, { add } from './pkg/without_a_bundler.js';
async function run() {
// First up we need to actually load the Wasm file, so we use the
// default export to inform it where the Wasm file is located on the
// server, and then we wait on the returned promise to wait for the
// Wasm to be loaded.
//
// It may look like this: `await init('./pkg/without_a_bundler_bg.wasm');`,
// but there is also a handy default inside `init` function, which uses
// `import.meta` to locate the Wasm file relatively to js file.
//
// Note that instead of a string you can also pass in any of the
// following things:
//
// * `WebAssembly.Module`
//
// * `ArrayBuffer`
//
// * `Response`
//
// * `Promise` which returns any of the above, e.g. `fetch("./path/to/wasm")`
//
// This gives you complete control over how the module is loaded
// and compiled.
//
// Also note that the promise, when resolved, yields the Wasm module's
// exports which is the same as importing the `*_bg` module in other
// modes
await init();
// And afterwards we can use all the functionality defined in wasm.
const result = add(1, 2);
console.log(`1 + 2 = ${result}`);
if (result !== 3)
throw new Error("wasm addition doesn't work!");
}
run();
</script>
</body>
</html>
Note: You cannot directly open
index.html
in your web browser due to CORS limitations. Instead, you can set up a quick development environment using Python's built-in HTTP server:wasm-pack build --target web python3 -m http.server 8080
If you don't have Python installed, you can also use miniserve which is installable via Cargo:
cargo install miniserve miniserve . --index "index.html" -p 8080
And that's it! Be sure to read up on the deployment options to see what it means to deploy without a bundler.
Using the older --target no-modules
The older version of using wasm-bindgen
without a bundler is to use the
--target no-modules
flag to the wasm-bindgen
CLI.
While similar to the newer --target web
, the --target no-modules
flag has a
few caveats:
- It does not support local JS snippets
- It does not generate an ES module
- It does not support
--split-linked-modules
outside of a document, e.g. inside a worker
With that in mind the main difference is how the wasm/JS code is loaded, and
here's an example of loading the output of wasm-pack
for the same module as
above.
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<!-- Include the JS generated by `wasm-pack build` -->
<script src='pkg/without_a_bundler_no_modules.js'></script>
<script>
// Like with the `--target web` output the exports are immediately
// available but they won't work until we initialize the module. Unlike
// `--target web`, however, the globals are all stored on a
// `wasm_bindgen` global. The global itself is the initialization
// function and then the properties of the global are all the exported
// functions.
//
// Note that the name `wasm_bindgen` can be configured with the
// `--no-modules-global` CLI flag
const { add } = wasm_bindgen;
async function run() {
await wasm_bindgen();
const result = add(1, 2);
console.log(`1 + 2 = ${result}`);
}
run();
</script>
</body>
</html>