我们将通过一个简单的示例,让你能够在 Hugo 网站的客户端运行 Rust 代码。我们将把 Rust 代码编译成 WebAssembly(wasm),从而在浏览器中获得接近原生的性能!
创建一个新的 Hugo 站点
首先,让我们初始化 Hugo 的快速启动站点:
hugo new site quickstart_wasm
cd quickstart_wasm
git init
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
echo "theme = 'ananke'" >> hugo.toml
hugo server
你应该会看到类似以下的输出:
# 日志 ...
构建时间:28 毫秒
环境: "development"
从磁盘提供页面
运行在快速渲染模式。如需在更改时完全重建:hugo server --disableFastRender
Web 服务器已启动,访问地址:http://localhost:55802/ (绑定地址 127.0.0.1)
如果你打开 localhost
的链接,应该会看到类似这样的页面:
创建 Rust 项目
我们将使用 Rust 作为 wasm 的源语言,主要是因为它的宏使得创建绑定变得极其简单。为了方便起见,让我们在 Hugo 站点的 assets
目录中初始化这个库。
# 在 Hugo 站点文件夹中
cd assets
mkdir rust_app && cd rust_app
cargo init --lib
打开 Cargo.toml
并添加以下内容
# 在底部添加
[lib]
crate-type = ["cdylib", "rlib"]
现在,我们需要添加 wasm-bindgen
作为依赖项,这将为我们提供一种单行解决方案来创建绑定。
cargo add wasm-bindgen
在 src/lib.rs
中,让我们编写一个性能关键的函数,我们需要从我们的 web 应用程序中调用它。
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn sieve_of_eratosthenes(n: usize) -> Vec<i32> {
let mut primes = Vec::new();
let mut is_prime = vec![true; n + 1];
is_prime[0] = false;
is_prime[1] = false;
for i in 2..=n {
if is_prime[i] {
primes.push(i as i32);
let mut j = 2 * i;
while j <= n {
is_prime[j] = false;
j += i;
}
}
}
primes
}
我们将使用以下命令构建它
wasm-pack build --target web
你应该会在 pkg
中看到编译后的输出。
ls pkg
package.json rust_app.d.ts rust_app.js rust_app_bg.wasm rust_app_bg.wasm.d.ts
从 JavaScript 调用 Rust
现在,在 rust_app
文件夹中,让我们创建一个将使用我们的素数筛的 Web 应用。
# 在 assets/rust_app 中
mkdir www && cd www
touch index.js
将以下内容放入 index.js 文件中
import init, * as wasm from '../pkg/rust_app';
init(wasm_path).then(_ => {
function computePrimes()
{
var inputNumber = parseInt(
document.getElementById('inputNumber').value,
);
if (!isNaN(inputNumber) && inputNumber >= 1) {
var primes = wasm.sieve_of_eratosthenes(inputNumber);
document.getElementById('output').innerText = primes.join(', ');
} else {
document.getElementById('output').innerText =
'请输入一个有效的整数。';
}
}
let button = document.getElementById('computeButton');
button.addEventListener('click', () => {
computePrimes();
});
});
将WebAssembly嵌入网站
现在,我们希望将wasm函数的输出显示在网站上。因此,让我们创建一个可以在文章中插入的小型Web应用的短代码。
在 ~/quickstart_wasm/layouts/shortcodes/wasm_app.html
中:
<!doctype html>
<html lang="en">
<head>
<title>质数查找器</title>
</head>
<body>
<input type="number" id="inputNumber" placeholder="输入一个整数..." />
<button id="computeButton">计算</button>
<div id="output"></div>
<!-- rust_app 必须在 /assets 目录下才能被检测到! -->
{{ $wasm_path := resources.Get "rust_app/pkg/rust_app_bg.wasm" }}
<script>
wasm_path = "{{ $wasm_path.Permalink }}";
</script>
{{ $index_js := resources.Get "rust_app/www/index.js" | js.Build }}
<script type="module" src="{{ $index_js.Permalink }}"></script>
</body>
</html>
这里的关键部分是在JS环境中设置全局变量 wasm_path
。这做了两件事:
- 告诉Hugo通过
fetch
请求使该路径可访问 - 为JS脚本提供要执行的wasm字节码
现在我们应该能够从主页使用质数生成器了!
hugo server
– 从Rust生成的质数
结论
网上关于 wasm + Hugo 的资源并不多,所以我想我应该写第一个教程。如果你有替代/更好的方法来部署 wasm,请在评论中告诉我。