NiQin (blog: 泥芹) shared the aphorism --
I returned and saw under the sun, that the race is not to the swift, nor the battle to the strong, neither yet bread to the wise, nor yet riches to men of understanding, nor yet favour to men of skill; but time and chance happeneth to them all. -- 《圣经》

[WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端(2)- 组件和路由

💥 内容涉及著作权,均归属作者本人。若非作者注明,默认欢迎转载:请注明出处,及相关链接。

Summary: 《Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端》系列文章第二部分:实现目标、crate 引入、IDE 配置、yew 组件开发、yew 路由定义,以及应用入口组件等。

Topics: rust graphql webassembly wasm yew trunk yew-router

上一篇文章《起步及 crate 选择》中,我们介绍了选型原因,介绍了构建工具,以及搭建了 yew 的基本开发环境并测试。

本篇文章中,我们将开始 Yew 编码开发。我们本系列文章,侧重于实践体验。因此,文章中可能会对某些 yew 基础知识和需要注意的地方进行详细说明。但整体而言,对于 yew 的基础知识,笔者不做过多介绍。Yew 的基础资料很详实,其中文文档的国内在线阅读,请访问 https://yew.budshome.com。

需要说明的是,Yew 的基础知识方面,官方提供了很充实的资料,笔者不进行照抄和搬运。感兴趣的朋友可自行阅读;因为笔者的官网访问,一周总有几次着急查阅时,页面打不开,所以笔者在国内架设 Yew 中文文档的在线阅读服务器 https://yew.budshome.com。

实践目标

在以前的 web 前端开发中,笔者使用 tide + handlebars-rust + graphql_client 技术组合:其中 graphql_client 用于从 GraphQL 服务后端获取 GraphQL 数据,转换为 json 格式;json 格式数据可直接用于 handlebars-rust 模板引擎,做以展示;tide 作为服务器,用于路由分发,逻辑处理等。即 mvc 开发模式,这是传统的 web 开发方式。实现结果如下图所示:

实践目标

我们本次使用 yew 的实践,也希望实现相同的目标结果。

crate 引入

yew 是单页面开发方式,但我们希望实现三个数据的展示:主界面和导航菜单、用户列表,以及项目列表。获取 GraphQL 数据并解析的方式,我们放在下篇文章讲解。本篇文章中,我们仅开发主界面和导航组件、用户列表组件,以及项目列表组件。到访各自列表的路由,我们通过 yew-router 实现。当然,组件的开发,使用 yew

通过 cargo-edit 工具,我们将上述 crate 加入到 Carto.toml 中,使用各个 crate 的最新版本。

cargo add yew-router

上篇文章中,我们引入了 wasm-bindgenyew 两个 crate,所以 Carto.toml 文件总体内容如下(笔者根据习惯,通常会做些顺序的调整或添加空行,这无关紧要):

[package]
name = "frontend-yew"
version = "0.1.0"
authors = ["我是谁?"]
edition = "2018"

[dependencies]
yew = "0.18.0"
yew-router = "0.15.0"

wasm-bindgen = "0.2.74"

IDE 配置

俗话说,工欲善其事,必先利其器。yew 编码方面,一些 IDE 提供的特性可以给编码开发提供很大的辅助作用。目前,主要包括组件模板,以及为 yew 中的 html! 宏启用 HTML 代码的智能感知。

具体到 Rust IDE 方面,比较好使的集成开发环境主要有 vsCode 和 IntelliJ-Rust。IntelliJ-Rust 相对来说更智能一些;但笔者使用的是 vsCode,因为喜欢更多的手动操作。

yew 组件模板

vsCode

  • 导航菜单:File > Preferences > User Snippets;
  • 选择 Rust 语言;
  • 在 json 文件中,增加如下代码片段:

具体 Rust 代码之外的部分,可以使之中文化。

{
    "Create new Yew component": {
        "prefix": "yew component",
        "body": [
            "use yew::prelude::*;",
            "",
            "pub struct ${1} {",
            "    link: ComponentLink<Self>,",
            "}",
            "",
            "pub enum Msg {",
            "}",
            "",
            "impl Component for ${1} {",
            "    type Message = Msg;",
            "    type Properties = ();",
            "",
            "    fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {",
            "        Self {",
            "            link,",
            "        }",
            "    }",
            "",
            "    fn update(&mut self, msg: Self::Message) -> ShouldRender {",
            "        match msg {}",
            "    }",
            "",
            "    fn change(&mut self, _props: Self::Properties) -> ShouldRender {",
            "        false",
            "    }",
            "",
            "    fn view(&self) -> Html {",
            "        html! {",
            "            ${0}",
            "        }",
            "    }",
            "}"
        ],
        "description": "Create a new Yew component without properties but with a message enum"
    }
}

使用方法:

  • Rust 文件的光标处,按 F1 键打开控制面板,然后输入snippet;
  • 输入上述代码片段中的 prefix 定义值,或者直接选择模板。

IntelliJ-Rust

  • 导航菜单:File | Settings | Editor | Live Templates;
  • 选择 Rust 并点击加号 + 图标,以增加模板;
  • 设定名字和描述;
  • 在模板文本中,粘贴如下代码片段:
use yew::prelude::*;

struct $NAME$ {
    link: ComponentLink<Self>
}

enum Msg {
}

impl Component for $NAME$ {
    type Message = Msg;
    type Properties = ();

    fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self { 
            link 
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {}
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        html! {
            $HTML$
        }
    }
}

为 yew 中的 html! 宏启用 HTML 代码的智能感知

此项辅助特性,intellij-rust 目前无法提供。

vsCode 中,虽然不支持指定的 html! 宏语法,但是可以在 settings.json 中添加如下配置,以启用 HTML 代码的智能感知:

"emmet.includeLanguages": {
    "rust": "html",
}

yew 组件开发

本篇文章中,我们先实现最简单的 yew 组件开发,仅局限于一下几个方面:主界面/用户列表界面/项目列表界面的组件开发、各个界面的路由定义,以及界面间的路由跳转。

另外,还有 web 前端的主程序入口组件,我们将其放到路由定义部分,一起讲述。

暂不实现具体界面的数据获取、解析,以及展示;也未定义任何样式。

本篇文章仅是理解 yew 的启动、组件,以及路由。

yew 中,最基础的即是组件,其可管理自己的状态,并可以渲染为 DOM。组件是通过实现结构体的 Component trait 来创建的。

我们在 src 目录中,创建 pages 文件夹,并创建 mod.rshome.rsusers.rs,以及 projects.rs 四个文件。

mkdir ./src/pages
cd ./src/pages
touch mod.rs home.rs users.rs projects.rs

并编辑 mod.rs 文件,代码如下:

pub mod home;
pub mod users;
pub mod projects;

用户列表组件

上文提到,组件是通过实现结构体的 Component trait 来创建的。所以用户列表组件的创建,非常简单:

  • 定义一个 Users 结构体;
  • 为其实现 Component trait;
  • Message 表示组件可以处理以触发某些副作用的各种消息;
  • Properties 表示从父级组件传递到子级组件的信息;
  • 根据 yew 组件的声明周期,我们初始,至少需要实现 4 个方法:
    • create,组件创建方法,返回值为 Self
    • update,组件更新方法,返回值为 bool 类型的 ShouldRender。暂时我们未有实现,通过 unimplemented! 宏返回 false
    • change,组件更改方法,返回值为 bool 类型的 ShouldRender。同上,暂时我们未有实现,通过 unimplemented! 宏返回 false
    • view,组件展示为 DOM 的方法,返回值为 yew 中的 Html 类型,实质上是 VNode 枚举。

好的,在 users.rs 文件中,我们定义结构体并对之实现组件特质(trait):

use yew::prelude::*;

pub struct Users;

impl Component for Users {
    type Message = ();
    type Properties = ();

    fn create(_props: Self::Properties, _: ComponentLink<Self>) -> Self {
        Self
    }

    fn update(&mut self, _msg: Self::Message) -> ShouldRender {
        unimplemented!()
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        unimplemented!()
    }

    fn view(&self) -> Html {
        html! {
            <div>
            { "用户列表 - 蓝色" }
            </div>
        }
    }
}

项目列表组件、主界面组件

项目列表组件、主界面组件,同用户列表组件的定义完全一致,仅需要替换 users/Usersprojects/Projectshome/Home,然后复制到 projects.rshome.rs 文件中。

projects.rs 文件内容如下:

use yew::prelude::*;

pub struct Projects;

impl Component for Projects {
    type Message = ();
    type Properties = ();

    fn create(_props: Self::Properties, _: ComponentLink<Self>) -> Self {
        Self
    }

    fn update(&mut self, _msg: Self::Message) -> ShouldRender {
        unimplemented!()
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        unimplemented!()
    }

    fn view(&self) -> Html {
        html! {
            <div>
            { "项目列表 - 绿色" }
            </div>
        }
    }
}

不要忘了填充 home.rs 文件,下述我们要用到。

yew 路由定义,以及应用入口组件

yew 的路由,通过 yew-router 库实现。通常的应用模式为,定义一个枚举,对其需要进行属性派生 #[derive(Switch, Debug, Clone, PartialEq)]:关键的是 SwitchPartialEqSwitch trait 用于在该 trait 的实现者之间进行 Route 转换。PartialEq 是 yew 中的一个性能优化,避免组件的重新渲染。

对于枚举字段,应用属性注解语法 #[to = "<path>"],表示具体的分发路由。如本例中,我们如下定义:

#[derive(Switch, Debug, Clone, PartialEq)]
pub enum Route {
    #[to = "/users"]
    Users,
    #[to = "/projects"]
    Projects,
    #[to = "/"]
    Home,
}

重要:当前 yew-router 库的版本中,#[to = "/"] 必须放置在最后,否则其之后的 #[to = "/<path>"] 均无法正常工作。

如下定义是无法按照预期执行的,也许愿意测试一下:

#[derive(Switch, Debug, Clone, PartialEq)]
pub enum Route {
    #[to = "/"]
    Home,
    #[to = "/users"]
    Users,
    #[to = "/projects"]
    Projects,
}

但在 yew-router 库的开发版本中,此问题会解决。并且,新的版本中,会引入 Routable trait、属性 #[to = "<path>"] 会变为 #[at("<path>")]。我们的定义,在新的版本中,如下代码所示:

#[derive(Routable, PartialEq, Clone, Debug)]
pub enum Route {
    #[at("/")]
    Home,
    #[at("/users")]
    Users,
    #[at("/projects")]
    Projects,
}

看起来更符合 Rust 语言风格。但目前版本是不可用的,仅作了解。

main.rs 中,我们还需要组合上一节定义的界面组件,将其组合展示。因此,我们需要定义一个新的 yew 主程序入口组件,作为 web 前端应用的入口。

入口组件的定义方式中,我们要引入 yew-router 库提供的一个标签组件 RouterAnchor,该标签组件提供一个点击响应,可按照定义的路由进行导航。

yew-router 的新版本中,语法更简洁一些,想必性能也有提升。

另外,还有布局,和 JSX 语法扩展应用大抵相同。嗯,大概需要说明的就是这些,我们看看 main.rs 的完整代码:

use yew::prelude::*;
use yew_router::prelude::*;
use yew_router::components::RouterAnchor;

mod pages;
use pages::{home::Home, users::Users, projects::Projects};

#[derive(Switch, Debug, Clone, PartialEq)]
pub enum Route {
    #[to = "/users"]
    Users,
    #[to = "/projects"]
    Projects,
    #[to = "/"]
    Home,
}

struct App;

impl Component for App {
    type Message = ();
    type Properties = ();

    fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
        Self
    }

    fn update(&mut self, _: Self::Message) -> bool {
        unimplemented!()
    }

    fn change(&mut self, _: Self::Properties) -> bool {
        unimplemented!()
    }

    fn view(&self) -> Html {
        type Anchor = RouterAnchor<Route>;

        html! {
            <>
            <div>
                { "tide-async-graphql-mongodb / frontend-yew" }
            </div>
            <div>
                <Anchor route=Route::Users>
                    { "用户列表" }
                </Anchor>
                { " - " }
                <Anchor route=Route::Projects>
                    { "项目列表" }
                </Anchor>
                { " - " }
                <Anchor route=Route::Home>
                    { "主页" }
                </Anchor>
            </div>
            <main>
                <Router<Route, ()>
                    render = Router::render(|switch: Route| {
                        match switch {
                            Route::Users => html!{ <Users/> },
                            Route::Projects => html!{ <Projects/> },
                            Route::Home => html!{ <Home/> },
                        }
                    })
                />
            </main>
            </>
        }
    }
}

fn main() {
    yew::start_app::<App>();
}

好,代码到此结束。让我们运行一下,看看成果。

运行

执行 trunk serve 命令,浏览器会自动打开一个页面,或者手动在浏览器中访问 http://127.0.0.1:3001。如果你未按照上篇 trunk.toml 所介绍的配置,请访问你自定义的端口(默认为 8080)。

点击导航菜单,可以看到页面内容发生了改变,本文的目标已经达成。

注:或许你会发现一些小 bug,但请暂且容忍。

谢谢您的阅读!


Related Articles

  1. [Rust] RustHub.org:基于 Rust-Web 技术栈,及 image-rs、fluent-rs、rhai-script ……
  2. [WebAssembly] yew SSR 服务器端渲染
  3. [Rust] async-std 创建者对于最近“项目是否已死?”,移除对其支持等的答复
  4. [Rust] Rust 1.56.0 版本和 Rust 2021 版次发布,新特性一览,及项目的迁移、升级
  5. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 博客应用的体验报告
  6. [Rust] Rust 官方周报 399 期(2021-07-14)
  7. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(5)- 构建 HTTP 请求、与外部服务器通信的两种方法
  8. [Rust] Rust 官方周报 398 期(2021-07-07)
  9. [Rust] Rust 官方周报 397 期(2021-06-30)
  10. [Rust] Rust 官方周报 396 期(2021-06-23)
  11. [Rust] Rust 官方周报 395 期(2021-06-16)
  12. [Rust] Rust 1.53.0 明日发布,关键新特性一瞥
  13. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(3)- rhai 脚本、静态/资源文件、环境变量等
  14. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(2)- 获取并解析 GraphQL 数据
  15. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(1)- crate 选择及环境搭建
  16. [Rust] Rust 官方周报 394 期(2021-06-09)
  17. [Rust] Rust web 前端库/框架评测,以及和 js 前端库/框架的比较
  18. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(4)- 获取 GraphQL 数据并解析
  19. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(3)- 资源文件及小重构
  20. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端(2)- 组件和路由
  21. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端(1)- 起步及 crate 选择
  22. [Rust] Rust 官方周报 393 期(2021-06-02)
  23. [Rust] Rust 官方周报 392 期(2021-05-26)
  24. [Rust] Rust 中,对网址进行异步快照,并添加水印效果的实践
  25. [Rust] Rust 官方周报 391 期(2021-05-19)
  26. [Rust] Rust,风雨六载,砥砺奋进
  27. [Rust] 为什么我们应当将 Rust 用于嵌入式开发?
  28. [Rust] Rust 官方周报 390 期(2021-05-12)
  29. [Rust] Rust + Android 的集成开发设计
  30. [Rust] Rust 1.52.1 已正式发布,及其新特性详述
  31. [Rust] 让我们用 Rust 重写那些伟大的软件吧
  32. [Rust] Rust 1.52.0 已正式发布,及其新特性详述
  33. [Rust] Rust 官方周报 389 期(2021-05-05)
  34. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(4) - 变更服务,以及小重构
  35. [Rust] Rust 1.52.0 稳定版预发布测试中,关键新特性一瞥
  36. [Rust] Rust 生态中,最不知名的贡献者和轶事
  37. [Rust] Rust 基金会迎来新的白金会员:Facebook
  38. [Rust] Rustup 1.24.1 已官宣发布,及其新特性详述
  39. [Rust] Rust 官方周报 388 期(2021-04-28)
  40. [Rust] Rust 官方周报 387 期(2021-04-21)
  41. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(4)- 变更服务,以及第二次重构
  42. [Rust] Rustup 1.24.0 已官宣发布,及其新特性详述
  43. [Rust] basedrop:Rust 生态中,适用于实时音频的垃圾收集器
  44. [Rust] Rust 编译器团队对成员 Aaron Hill 的祝贺
  45. [Rust] Jacob Hoffman-Andrews 加入 Rustdoc 团队
  46. [机器人] 为什么应将 Rust 引入机器人平台?以及机器人平台的 Rust 资源推荐
  47. [Rust] rust-lang.org、crates.io,以及 docs.rs 的管理,已由 Mozilla 转移到 Rust 基金会
  48. [Rust] Rust 官方周报 386 期(2021-04-14)
  49. [Rust] Rust 编译器(Compiler)团队 4 月份计划 - Rust Compiler April Steering Cycle
  50. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(3) - 重构
  51. [Rust] 头脑风暴进行中:Async Rust 的未来熠熠生辉
  52. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(2) - 查询服务
  53. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务 - 起步及 crate 选择
  54. [Rust] Rust 2021 版本特性预览,以及工作计划
  55. [Rust] Rust 用在生产环境的 42 家公司
  56. [Rust] 构建最精简的 Rust Docker 镜像
  57. [Rust] Rust 官方周报 385 期(2021-04-07)
  58. [Rust] 使用 Rust 做异步数据采集的实践
  59. [Rust] Android 支持 Rust 编程语言,以避免内存缺陷
  60. [Rust] Android 平台基础支持转向 Rust
  61. [Rust] Android 团队宣布 Android 开源项目(AOSP),已支持 Rust 语言来开发 Android 系统本身
  62. [Rust] RustyHermit——基于 Rust 实现的下一代容器 Unikernel
  63. [Rust] Rustic:完善的纯粹 Rust 技术栈实现的国际象棋引擎,多平台支持(甚至包括嵌入式设备树莓派 Raspberry Pi、Buster)
  64. [Rust] Rust 迭代器(Iterator trait )的要诀和技巧
  65. [Rust] 使用 Rust 极致提升 Python 性能:图表和绘图提升 24 倍,数据计算提升 10 倍
  66. [Rust] 【2021-04-03】Rust 核心团队人员变动
  67. [Rust] Rust web 框架现状【2021 年 1 季度】
  68. [Rust] Rust 官方周报 384 期(2021-03-31)
  69. [Rust] Rust 中的解析器组合因子(parser combinators)
  70. [生活] 毕马威(KPMG)调查报告:人工智能的实际采用,在新冠疫情(COVID-19)期间大幅提升
  71. [Python] HPy - 为 Python 扩展提供更优秀的 C API
  72. [Rust] 2021 年,学习 Rust 的网络资源推荐(2)
  73. [Rust] 2021 年,学习 Rust 的网络资源推荐
  74. [生活] 况属高风晚,山山黄叶飞——彭州葛仙山露营随笔
  75. [Rust] Rust 1.51.0 已正式发布,及其新特性详述
  76. [Rust] 为 Async Rust 构建共享的愿景文档—— Rust 社区的讲“故事”,可获奖
  77. [Rust] Rust 纪元第 382 周最佳 crate:ibig 的实践,以及和 num crate 的比较
  78. [Rust] Rust 1.51.0 稳定版本改进介绍
  79. [Rust] Rust 中将 markdown 渲染为 html
  80. [生活] 国民应用 App 的用户隐私数据窥探
  81. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(3)- 重构
  82. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(2)- 查询服务
  83. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(1)- 起步及 crate 选择
  84. [Rust] Rust 操控大疆可编程 tello 无人机

Topics

rust(84)

graphql(17)

rust-官方周报(17)

webassembly(16)

wasm(10)

tide(9)

async-graphql(9)

yew(9)

rust-web(8)

rust-官方博客(8)

this-week-in-rust(6)

mysql(5)

actix-web(5)

rbatis(5)

android(4)

mongodb(3)

json-web-token(3)

jwt(3)

cargo(3)

技术延伸(3)

rust-wasm(3)

trunk(3)

handlebars(3)

rhai(3)

async-std(3)

用户隐私(2)

学习资料(2)

python(2)

ai(2)

人工智能(2)

postgresql(2)

rust-compiler(2)

rust-基金会(2)

rust-foundation(2)

rustup(2)

rust-toolchain(2)

rust-工具链(2)

rust-游戏开发(2)

rust-区块链(2)

rust-2021(2)

graphql-client(2)

surf(2)

rust-game(2)

rusthub(2)

tello(1)

drone(1)

无人机(1)

隐私数据(1)

markdown(1)

html(1)

crate(1)

async(1)

异步(1)

旅游(1)

不忘生活(1)

葛仙山(1)

hpy(1)

python-扩展(1)

正则表达式(1)

解析器组合因子(1)

组合器(1)

regular-expression(1)

parser-combinator(1)

regex(1)

官方更新(1)

rust-工作招聘(1)

rust-技术资料(1)

rust-周最佳-crate(1)

rust-web-框架(1)

rust-web-framework(1)

rust-核心团队(1)

rust-core-team(1)

rust-language-team(1)

pyo3(1)

rust-python-集成(1)

python-性能改进(1)

迭代器(1)

iterator-trait(1)

国际象棋(1)

chess(1)

游戏引擎(1)

game-engine(1)

虚拟化(1)

unikernel(1)

rustyhermit(1)

linux(1)

virtualization(1)

sandboxing(1)

沙箱技术(1)

数据采集(1)

异步数据采集(1)

docker(1)

镜像(1)

生产环境(1)

rust-评价(1)

rust-2021-edition(1)

rust-2021-版本(1)

graphql-查询(1)

vision-doc(1)

愿景文档(1)

代码重构(1)

steering-cycle(1)

方向周期(1)

隐私声明(1)

机器人(1)

robotics(1)

rustdoc(1)

rust-编译器(1)

实时音频(1)

real-time-audio(1)

变更服务(1)

mutation(1)

查询服务(1)

query(1)

rust-贡献者(1)

rust-轶事(1)

rust-稳定版(1)

rust-预发布(1)

rust-测试(1)

安全编程(1)

可信计算(1)

安全代码(1)

secure-code(1)

rust-android-integrate(1)

rust-embedded(1)

rust-嵌入式(1)

rust-生产环境(1)

rust-production(1)

网页快照(1)

网页截图(1)

水印效果(1)

图片水印(1)

yew-router(1)

css(1)

web-前端(1)

wasm-bindgen(1)

区块链(1)

blockchain(1)

dotenv(1)

标识符(1)

rust-1.53.0(1)

rust-1.56.0(1)

rust-项目升级(1)

异步运行时(1)

ssr(1)

tokio(1)

warp(1)

reqwest(1)

graphql-rust(1)


Elsewhere

- Open Source
  1. github/zzy
  2. github/sansx
- Learning & Studying
  1. Rust 学习资料 - 泥芹