前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(1)- crate 选择及环境搭建

使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(1)- crate 选择及环境搭建

作者头像
niqin.com
发布2022-09-01 15:48:20
1.7K0
发布2022-09-01 15:48:20
举报
文章被收录于专栏:Rust 生态与实践

目前,web 前端开发方面,通常有两种技术组合:一种是使用模板引擎,主要在服务器端渲染,这种方式对 seo 有较高要求的应用有利;同时,在后续优化方面,也较有优势。另一种则是前端框架,如 yew、react、vue、seed 一类,采用声明式设计;在保证性能下限的前提下,高效且灵活地进行快速开发。

另外,具体到 yew、react、vue、seed 来说,也有所不同:yew、seed 则是 WebAssembly 框架。WebAssembly 是 W3C 组织于 2019 年 12 月下旬才发布的新标准,还处于发展初期。前景或许更广阔一些,但目前落地的应用场景还比较罕见。

前时的文章《Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端》,即是对 Rust 生态中 WebAssembly 框架的实践。放眼整个 web 前端开发,都可以说是比较新颖的技术。但是对于生产环境,其小规模使用,或许都是一个挑战。如果你想使用 Rust 技术栈开发 web 应用,目前还是采用模板引擎的组合,较为稳妥一些。

实践目标

在以前的构建 Rust 异步 GraphQL 服务系列中,分别采用 tide + async-graphql + mongodbactix-web + async-graphql + rbatis + postgresql / mysql 开发了 GraphQL 服务后端。感兴趣的朋友可以参阅博文——

本次实践中,即是基于 Rust 技术生态,采用模板引擎,来实现 Rust web 前端的开发。实践过程中,我们通过 GraphQL 服务后端 API,获取 GraphQL 数据并解析。然后,在页面中,对用户列表、项目列表做以展示。

crate 的选择

Rust 生态中,成熟的模板引擎库非常多。askama 模板引擎的开发者,对下述出现较早的模板库进行了极其简单的测评,有兴趣可以参考 djc/template-benchmarks-rs:

  • write!:基于标准库 write! 宏实现
  • handlebars:handlebars 模板的 Rust 实现
  • tera:基于 jinja2/django 模板规范
  • liquid:liquid 模板的 Rust 实现
  • askama:类型安全的、类 jinja 的编译型模板
  • horrorshow:使用 Rust 宏实现的模板
  • ructe:高效、类型安全的编译型模板
  • fomat:使用类 print/write/format 宏实现的小型模板
  • markup:快速、类型安全的模板
  • maud:Rust 实现的编译时 HTML 模板引擎
  • sailfish:简单、小型、快速的模板引擎

上述列表所提及模板,仅为开发较早,askama 模板引擎的开发者对其测评。另外,比较成熟的 Rust 模板引擎还有 mustache 规范的 rustache、rust-mustache,以及微型而极快的 tinytemplate 等等。

本系列文章,笔者选择了 handlebars-rust 模板引擎。评测中,其基准测试结果并不出众,但评测都有其局限性,仅可参考。而 handlebars-rust 对 rhai(Rust 的嵌入式脚本引擎)的支持方面,笔者非常感兴趣,是故选择。

HTTP 服务器框架,笔者选择了轻型的 tide(中文文档)。但是如果你对 actix-web 或者其它服务器端框架更感兴趣,或者想替换也是非常容易的,因为 cookie、GraphQL 客户端等代码都是通用的。

HTTP 客户端框架,笔者选择了 surf。如果你想使用 reqwest,替换仅为一行代码(将发送 GraphQL 请求时的 surf 函数,修改为 reqwest 函数即可)。

项目中,rhai(Rust 的嵌入式脚本引擎),主要用于开发页面脚本,作为 JavaScript 的一个替代方案。

嗯,本次实践用到的主要 crate,大概就是这些。

工程的创建

我们从零开始,进行本次实践。

在我们的实践项目根目录 tide-async-graphql-mongodb 或者 actix-web-async-graphql-rbatis 中,创建新的新的工程 frontend-handlebars。

GraphQL 服务后端,开源在 github,可以访问如下仓库获取源码:

  • tide-async-graphql-mongodb(本博客即在此仓库基础上扩展实现)
  • actix-web-async-graphql-rbatis
代码语言:javascript
复制
cd tide-async-graphql-mongodb # 或 actix-web-async-graphql-rbatis
    cargo new frontend-handlebars --vcs none

同时,需要在根目录的 Cargo.toml(不是 frontend-handlebars 目录中的 Cargo.toml)将 frontend-handlebars 项目添加到 workspace 部分:

代码语言:javascript
复制
[workspace]
members = [
    "./backend", 
    "./frontend-handlebars", 
    "./frontend-yew"
]

开发环境的配置

本文中,我们先进行开发环境的基础配置,整合各个 crate,并运行展示一个包含 handlebars 模板语法的 HTML 文件即可。因此,目前需要的主要 crate 仅为 tide、async-std,以及 handlebars-rust;另外,serdeserde_json crate 也是需要的。其中,async-std 需要启用特性 attributes,而 serde 需要启用特性 derive。我们使用 cargo-edit 工具,将它们加入到 frontend-handlebars 工程中。

代码语言:javascript
复制
cargo add async-std tide serde serde_json handlebars

此时,frontend-handlebars 项目中的 Cargo.toml 文件内容如下:

代码语言:javascript
复制
[package]
name = "frontend-handlebars"
version = "0.1.0"
authors = ["我是谁?"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-std = { version = "1.9.0", features = ["attributes"] }
tide = "0.16.0"

serde = { version = "1.0.126", features = ["derive"] }
serde_json = "1.0.64"

handlebars = "4.0.0"

代码开发

本文直接进入 Rust web 的开发演练,对于 Rust 的基础不做提及。

如果你没有 Rust 基础,《通过例子学 Rust》作为入门资料,是个很不错的选择。另外,机械工业出版社的《Rust 编程- 入门、实战与进阶》,非大块头的厚书。讲解了 Rust 核心语法后,注重编码能力训练,并且以 LeetCode 面试真题作为示例。

对于 handlebars 模板语法,我们也不做提及,官网资料很丰富,或者访问国内同步更新站点。

虽然仅是演练,但笔者不建议将代码一股脑写入 main.rs 中。我们划分模块,分层实现。

handlebars 模板

在 frontend-handlebars 目录下,创建放置模板文件、静态文件的目录:

代码语言:javascript
复制
cd frontend-handlebars
mkdir templates
touch templates/index.html

templates/index.html 是包含 handlebars 语法的模板文件:

代码语言:javascript
复制
<!doctype html>
<html lang="zh">

  <head>
    <title>{{ app_name }}</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">
  </head>

  <body>
    <center>
      <h1>{{ app_name }} </h1>
      <h3 style="padding-left: 20%;">-- {{ author }}</h3>

      <ul>
        <h2> &nbsp; </h2>
        <h2>
          <li><a href="/users">all users with token from graphql data</a></li>
        </h2>
        <h2>
          <li><a href="/projects">all projects from graphql data</a></li>
        </h2>
        <h2> &nbsp; </h2>
        <h2>
          <li><a href="http://127.0.0.1:8000/graphiql">graphql API</a></li>
        </h2>
      </ul>

    </center>
  </body>

</html>

模板渲染

对模板渲染,很显然是一个通用的处理过程。因此,我们将其抽象,放在通用类模块中。

代码语言:javascript
复制

模板的渲染抽象,主要是实现:规范模板路径、注册模板,以及对模板压入渲染数据。util/mod.rsutil/common.rs 2 个文件,代码如下:

util/mod.rs

代码语言:javascript
复制
pub mod common;

util/common.rs

代码语言:javascript
复制
use handlebars::Handlebars;
use serde::Serialize;
use tide::{http::mime::HTML, Body, Response, StatusCode};

pub struct Tpl<'tpl> {
    pub name: String,
    pub reg: Handlebars<'tpl>,
}

impl<'tpl> Tpl<'tpl> {
    pub async fn new(rel_path: &str) -> Tpl<'tpl> {
        let tpl_name = &rel_path.replace("/", "_");
        let abs_path = format!("./templates/{}.html", rel_path);

        // create the handlebars registry
        let mut hbs_reg = Handlebars::new();
        hbs_reg.register_template_file(tpl_name, abs_path).unwrap();

        Tpl {
            name: tpl_name.to_string(),
            reg: hbs_reg,
        }
    }

    pub async fn render<T>(&self, data: &T) -> tide::Result
    where
        T: Serialize,
    {
        let mut resp = Response::new(StatusCode::Ok);
        resp.set_content_type(HTML);
        resp.set_body(Body::from_string(
            self.reg.render(&self.name, data).unwrap(),
        ));

        Ok(resp.into())
    }
}

路由开发

路由,其定义就放在专门的路由模块中:

代码语言:javascript
复制
cd frontend-handlebars/src
mkdir routes
touch routes/mod.rs

也可以在定义一个 home.rs 或者 index.rs,然后将其引入 mod.rs

目前,仅一个页面,所以仅需定义一个路由处理函数,配置一个路由路径即可。所以我们直接将 index 路由处理函数放在 mod.rs 文件中。但是,后续的用户列表、项目列表路由处理,我们会放在各自的模块中。

handlebars 语法规则,可以直接接收 json 格式的数据并解析展示。因此,routes/mod.rs 文件中,我们定义要在模板中展示的数据。代码内容如下:

代码语言:javascript
复制
use tide::{self, Server, Request};
use serde_json::json;

use crate::{State, util::common::Tpl};

pub async fn push_res(app: &mut Server<State>) {

    app.at("/").get(index);
}

async fn index(_req: Request<State>) -> tide::Result {
    let index: Tpl = Tpl::new("index").await;

    // make data and render it
    let data = json!({"app_name": "frontend-handlebars - tide-async-graphql-mongodb", "author": "zzy"});

    index.render(&data).await
}

应用入口

main.rs 作为 web 应用的入口,需要读取路由模块的配置,并将其压入到服务器(Serve)结构体中。这点在 tideactix-web 中,概念是一致的,写法稍有差别。

Statetide 服务器的状态(State)结构体,用于存放一些和服务器具有相同生命周期的对象或值。actix-web 中,概念同样一致。笔者此书仅为示例,表示 tide 有此特性。

代码语言:javascript
复制
mod routes;
mod util;

#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
    // tide logger
    tide::log::start();

    // Initialize the application with state.
    // Something in Tide State
    let app_state = State {};
    let mut app = tide::with_state(app_state);
    // app = push_res(app).await;
    routes::push_res(&mut app).await;

    app.listen(format!("{}:{}", "127.0.0.1", "3000")).await?;

    Ok(())
}

//  Tide application scope state.
#[derive(Clone)]
pub struct State {}

编译和运行

执行 cargo buildcargo run 后,如果你未自定义端口,请在浏览器中打开 http://127.0.0.1:3000 。可以发现,handlebars 模板文件 templates/index.html 中的 HTML 元素:title、h1,以及 h3 的值来自路由处理函数 async fn index(_req: Request<State>)

至此,使用 handlebars 模板的 Rust web 前端开发环境已经搭建成功。

谢谢您的阅读,欢迎交流。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust 生态与实践 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实践目标
  • crate 的选择
  • 工程的创建
  • 开发环境的配置
  • 代码开发
    • handlebars 模板
      • 模板渲染
        • util/mod.rs
        • util/common.rs
      • 路由开发
        • 应用入口
        • 编译和运行
        相关产品与服务
        云服务器
        云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档