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 构建 web 前端(3)- 资源文件及小重构

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

Summary: 《Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端》系列文章第三部分:引入 css 样式表、图像等资源文件到 trunk 构建路径;以及精简 html! 宏中代码,提取为函数的代码重构等。

Topics: rust graphql webassembly wasm yew css

前两篇文章《起步及 crate 选择》《组件和路由》中,我们介绍了选型原因,搭建了 yew 的基本开发环境,并进行了最基础的组件和路由编码。并且和 yew 中文文档的翻译者 sansx 老师及一些感兴趣的朋友进行了友好而热烈的交流。

关于交流心得,笔者感觉有必要提及一下,作为一个即要走路也要看路的技术认知:

  • 关于 html! 宏中的 <>……</>,这是因为 html! 宏仅能有一个根标签元素。<>……</> 充当了一个根标签,输出实际上是空的。另外,html! 宏中的标签必须闭合,即使 html5 标准中不需要 /> 的自闭合标签,也不能省略 />。如 <img src="path" />
  • yew 生产环境的应用。笔者仅是 yew 的初学者,理解不很恰当。根据对官方 API 文档的理解,个人认为当前版本(yew 0.18)用于生产环境,是一个不小的挑战(包括开发和维护)。但从项目源码、issues 讨论,以及路线规划来看,个人认为下个版本(yew 0.19),差强人意。等待发布后,yew 0.19 用于个人或者团队的生产环境,是可以接受的。笔者也有此计划。
  • ssr 或者 seo 方面,yew 官方有计划,但未有实质进度。但笔者认为影响不大,网上几年前就有文章给出了结论:新时代的搜索引擎(Google、Yahoo、Bing、DuckDuckGo 等),能够像现代浏览器一样访问网站,能很好的抓取动态渲染后的内容,不用担心使用 yew 之类的框架而导致 seo 出现问题。几年过去了,搜索引擎的技术进步应该很大。再者,笔者认为现在信息传播的方式已经有所改变,国内尤为明显。最后,当国外搜索引擎已经收录大量中文站点的内容时,某些国内搜索引擎,却仅是首页甚至是未有收录;这样的情形,即使技术方面对 seo 很适配,估计也是不能解决收录问题的。
  • Rust 官方周报 393 期中有一篇技术,是关于 Rust + WebAssembly 为 deno 开发插件系统的,看起来前景很不错。基于 WebAssembly 的性能和特性,如果插件足够通用,说不定可发展为一个独立的职业。

前两篇文章中,我们实现的界面是非常简陋的,没有引入任何样式、图像等 web 应用必不可少的资源文件。本篇文章中,我们将实践如何对 yew 组件使用样式,组件包含图片等。严格来说,这部分是属于构建工具 trunk 的知识。trunk 工具在首篇文章《起步及 crate 选择》中已经提及,是完全的 Rust 技术栈开发,不同于 wasm-pack 那样需要 node 环境。其在样式方面,支持 css/sass/scss(scss 实质是 sass3 及之后的升级版,目前使用更广一些),我们都将进行实践。图像方面,笔者分别引入 icon 和在组件中放置 <img> 标签以作示例。其它 js 和数据等资源文件,未有设计,但使用和图像是类同的。

引入样式表

笔者在 frontend-yew 目录中,创建如下目录和结构,放置资源文件:

mkdir -p assets/{css, imgs, js, data}
cd assets/css
touch style.css style.sass style.scss

css 代码

我们分别有 css、sass,以及 scss,仅是为验证 trunk 对其都可以编译。

style.css

.home {
    background-color: red;
}

style.sass

$users-color: blue

.users
    background-color: $users-color;

style.scss

.logo-title {
    line-height: 40px;
    display:flex;
    align-items: center;
}

.nav {
    line-height: 30px;
    display:flex;
    align-items: center;
    margin-bottom: 20px;
    font-weight: bold;
}

$projects-color: green;

.projects {
    background-color: $projects-color;
}

将样式表加入 trunk 构建路径

trunk 工具构建时,资源文件通过 <link> 标签引入,但需要声明 data-trunk。我们要将上述三个样式表加入构建路径,在 index.html 文件中的 <head> 标签内,加入它们的路径:

<!doctype html>
<html lang="zh">

    <head>
        <title>tide-async-graphql-mongodb - frontend - yew</title>

        <meta charset="utf-8">

        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="author" content="zzy, https://github.com/zzy/tide-async-graphql-mongodb">

        <link data-trunk rel="css" href="assets/css/style.css">
        <link data-trunk rel="sass" href="assets/css/style.sass">
        <link data-trunk rel="scss" href="assets/css/style.scss">
    </head>

</html>

组件中使用 css

重要:以下均为代码片段,请注意文件名,以及不同的样式表压入方法。

使用 &str 字符串字面量

如在 main.rs 中的应用入口组件上,使用 style.scss 声明的样式:

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

        let home_cls = "nav";

        html! {
            <>
            <div class="logo-title">
                { "tide-async-graphql-mongodb / frontend-yew" }
            </div>
            <div class=home_cls>
                <Anchor route=Route::Users>
                    { "用户列表" }
                </Anchor>
                { " - " }
                ……
                ……
                ……
            </>
        }
    }

如在 users.rs 中的用户列表组件上,使用 style.sass 声明的样式:

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

使用 classes!

yew 的近期版本中,新增了 classes! 宏,让样式表的压入更灵活,扩展性更强。

如在 home.rs 中的主界面组件上,使用 style.css 声明的样式:

    fn view(&self) -> Html {
        let home_cls = "home";

        html! {
            <div class=classes!(home_cls)>
               { "主界面 - 红色" }
            </div>
        }
    }

如在 projects.rs 中的项目列表组件上,使用 style.scss 声明的样式:

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

引入图像

笔者向 assets 目录中放入一个 favicon.png 图像,向 assets/imgs 目录中放入一个 budshome.png 图像。

icon 和 <img> 都是通过 <link> 标签加入到构建路径,但 rel 属性则不同:icon 图像的引入,定义为 rel="icon",而 <img> 使用的图像资源,则要在构建时复制:可以选择复制单个文件,也可以复制文件夹。

<link data-trunk rel="icon" href="assets/favicon.png">

<link data-trunk rel="copy-dir" href="assets/imgs"> 
# 或者复制单个文件
<link data-trunk rel="copy-file" href="assets/imgs/budshome.png">

笔者使用的是复制文件夹。至此,index.html 文件完整内容为:

<!doctype html>
<html lang="zh">

    <head>
        <title>tide-async-graphql-mongodb - frontend - yew</title>

        <meta charset="utf-8">

        <link data-trunk rel="icon" href="assets/favicon.png">

        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="author" content="zzy, https://github.com/zzy/tide-async-graphql-mongodb">

        <link data-trunk rel="css" href="assets/css/style.css">
        <link data-trunk rel="sass" href="assets/css/style.sass">
        <link data-trunk rel="scss" href="assets/css/style.scss">

        <link data-trunk rel="copy-dir" href="assets/imgs">
    </head>

</html>

在 yew 组件代码中,我们直接嵌入图像元素,注意此时图像路径从的根目录为 imgs

注意html! 宏中的标签必须闭合,即使 html5 标准中不需要 /> 的自闭合标签,也不能省略 />

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

        let home_cls = "nav";

        html! {
            <>
            <div class="logo-title">
                <img src="imgs/budshome.png" />
                { "tide-async-graphql-mongodb / frontend-yew" }
            </div>
            <div class=home_cls>
                <Anchor route=Route::Users>
                    { "用户列表" }
                ……
                ……
                ……
            </>
        }
    }

运行和测试

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

点击导航菜单,可以看到页面内容有了一些基础的样式,也显示了图像元素,当然还是很简陋。但本文是示例说明资源文件的引入和构建,目标已经达成。

代码重构:精简 html! 宏中代码,提取为函数

有朋友联系,讨论 main.rs 文件中的 <main> 标签内代码是否为好的实践?是否应当提取为一个函数之类的?以保持 html! 宏中代码尽量精简。

笔者深以为然,函数相对来说是较好的实践。同时引申一下:yew 的新版本,增加了 yew-functional 函数组件包,目前还未发布为独立的 crate。

我们简单对其重构,增加一个 switch 函数,返回值为 yew 中的 Html 类型,实质上是 VNode 枚举。

fn switch(switch: Route) -> Html {
    match switch {
        Route::Users => {
            html! { <Users/> }
        }
        Route::Projects => {
            html! { <Projects/> }
        }
        Route::Home => {
            html! { <Home /> }
        }
    }
}

此时,main.rs 文件中的 <main> 标签内代码可精简为:

    <main>
        <Router<Route> render=Router::render(switch) />
    </main>

谢谢您的阅读!


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 学习资料 - 泥芹