EdwardElricNavigate back to the homepage

尝试用 Rust + Yew 写高性能前端页面

Edward Elric
January 5th, 2020 · 1 min read

前言

看到这篇文章,可能很多人会有个疑问:“已经有 React + TypeScript 这么好的组合,为什么还想着使用 Rust 来写前端页面,不折腾吗?” 首先值得讨论的一点,接下来几年的前端方向有哪些? 个人的愚见,在全栈领域必然是 serverless,我在业务开发中已经尝到甜头,高效、简便、心智负担很低了;而在 Web 领域,由于 2019 年 12 月 5 日— 万维网联盟(W3C)宣布 WebAssembly 核心规范成为正式标准,WASM 势必它的影响力会逐步提升。 WASM 的框架 / 库的选择已经比较丰富了,如:C# + Blazor、Go + Vugu、Rust + Yew 等等。 可能有人会问,为什么不能使用 TypeScript / JavaScript 来编译到 WASM,归根结底是因为 WASM 要求语言必须是静态强类型。当然可以魔改 TypeScript / JavaScript 做到这一点,例如 AssemblyScript / TurboScript 就是如此在 TypeScript / JavaScript 上做静态规范的。与其如此魔改,我个人觉得倒不如大大方方的使用静态强类型语言来编译到 WASM,反正魔改后也无法共享原有的生态。 那么为什么选择 Rust 而不是其他的诸如 Go、C#、C / C++ 呢?这个确实没有很有说服力的理由,只是出于个人的技术偏好,或者说是受到 RY 使用 Rust 实现 Deno 的鼓舞吧。

准备

环境陈述

我使用的是 macOS,很多人自然会想到使用 homebrew 来完成 Rust 环境 setup。一开始我也是这么做的,不过会遇到下面问题:

1error[E0463]: can't find crate for `std` | = note: the `wasm32-unknown-unknown` target may not be installed

最后还是走官方推荐的方式解决的。

Rust Installation

1curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Yew Installation

1cargo install wasm-pack
2cargo install cargo-web

上手

一些说明

Rust 本身就能实现编译到 WASM,使用 Yew 框架的原因就如其官网宣传:只是为了方便前端组件整合,以及对 JavaScript 互通性的考量。

项目初始化

这里不采用官网提供的参考,因为示例过于简单,有些脱离真正应用场景。下面的步骤是我个人认为比较接近真实开发的状态,相关源码我也放到 GitHub 供大家玩耍 ➡️https://github.com/SASUKE40/yew-starter

1git clone --depth=1 https://github.com/SASUKE40/yew-starter.git <project_name>

目录结构

1.
2├── Cargo.lock
3├── Cargo.toml
4├── LICENSE
5├── README.md
6├── index.html
7├── index.js
8├── package-lock.json
9├── package.json
10└── src
11 ├── app.rs
12 └── lib.rs

这里面最重要的就是 Cargo.toml 了,其中最关键的依赖如下:

1[lib]
2crate-type = ["cdylib"]
3
4[dependencies]
5wasm-bindgen = "0.2"
6yew = "0.10.0"

cdylib  是用于配置构建 C interoperability(C FFI),wasm-bindgen 依赖是用于 WASM 模块和 JavaScript 之间的交互粘合。

加载 WASM 以及 bind 的过程

1mod app;
2
3use wasm_bindgen::prelude::*;
4
5#[wasm_bindgen]
6pub fn run_app() -> Result<(), JsValue> {
7 yew::start_app::<app::App>();
8 Ok(())
9}

在需要导出 Rust 函数到 JavaScript 中,只需要在函数方法上注释 #[wasm_bindgen] 即可,接着在 js 文件中导入使用

1import { run_app } from './lib'
2run_app()

构建套件

使用 Parcel 是最方便的,开箱即用。既可以直接 import rs,也可以 import wasm。

image
我这里用了 parcel-plugin-wasm.rs 插件完成 Cargo loader 的工作,和上述 import rs 的方式不同:

1import { run_app } from './Cargo.toml'
2run_app()

Yew 组件编写体验

Yew 框架使用 html! macro 来生成 HTML

1impl Component for App {
2 type Message = Msg;
3 type Properties = ();
4
5 fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
6 App {}
7 }
8
9 fn update(&mut self, _msg: Self::Message) -> ShouldRender {
10 true
11 }
12 fn view(&self) -> Html<Self> {
13 html! {
14 <p>{ "Hello world!" }</p>
15 }
16 }
17}

模板中可以使用 html! { "Hello, World" } 这样的方式包裹文本或变量,这和 jsx 比较类似。 事件绑定、条件、循环也都是类似的,大同小异:

1// 点击事件
2html!{
3 <button onclick=|_| Msg::Click>{ "Click Me!" }</button>
4}
5
6// 条件渲染
7html! {
8 <div>
9 {
10 if show_link {
11 html! {
12 <a href="https://example.com">{"Link"}</a>
13 }
14 } else {
15 html! {}
16 }
17 }
18 </div>
19}
20
21// 迭代渲染
22html! {
23 <ul class="item-list">
24 { for self.props.items.iter().map(renderItem) }
25 </ul>
26}

效果

Navigate to https://yew-starter.netlify.com/

image
实际上 WASM 没有大家相信的那么“便携小巧”,WASM 吃掉的体积有 50kb。

总结

其优势:

  • WebAssembly 在桌面客户端移植到 Web 不可或缺
  • 作为胶水包存在,如某面包姐姐最近就在尝试 Rust 2 WASM(CPP) 2 Node.js 其疑点:
  • 高性能,感觉没有很突出到必须得这么做
  • 可移植性,要做 Web 的 JVM?

WASM 的生态日趋丰富完善,未来 WASM 在前端必然会大放异彩。不过也无需夸大其地位,可以参考 WebAssembly 的出现是否会取代 JavaScript?这个知乎回答。摘抄官方对其定位的一个回答:WebAssembly 旨在作为 JavaScript 的补充而不是替代。

image

通宵写文,求 🌟🌟 求关注,不胜感激 ⬇️

https://github.com/SASUKE40/yew-starter

Join our email list and get notified about new content

Be the first to receive our latest content with the ability to opt-out at anytime. We promise to not spam your inbox or share your email with any third parties.

More articles from Edward Elric

尝试一下 VSCode Remote 开发 Node 应用

VSCode 的 Remote SSH 插件完美击中运维痛点,以后可以抛弃 Vim 直接在 VSCode 里面改东西,非常方便。

May 17th, 2019 · 1 min read

试玩 deno2

前言 ry 近期把 deno 的第二版原型从 deno2 文件夹中转移到 src 上了,并初步完成之前定下针对 unprivileged 部分的改造目标: Use the gn build system for fast builds, sane configuration…

June 28th, 2018 · 1 min read
© 2015–2020 Edward Elric
Link to $https://twitter.com/sasuke688848Link to $https://github.com/sasuke40Link to $https://www.linkedin.com/in/sasuke/