Heidi Labs
GitHub TrendingGitHub Trending 项目约 10 分钟3,940 字

Topcoat:Rust 全栈框架

本文目录

Rust 的全栈框架

Topcoat 是一个模块化、开箱即用的 Rust 框架,用于构建全栈应用。它优先考虑简单性和生产力。参见 Learn Topcoat 开始使用,或查看 Roadmap 了解接下来会有什么。

早期阶段且处于实验状态。预计会有破坏性变更。

use topcoat::{
    Result,
    context::Cx,
    router::{module_router, page},
    view::{View, component, view},
};

#[tokio::main]
async fn main() {
    topcoat::start(module_router!().build()).await.unwrap();
}

#[page]
async fn home(cx: &Cx) -> Result<impl View> {
    Ok(view! {
        <!DOCTYPE html>
        <html>
        <body>
            hello(name: "World")
        </body>
        </html>
    })
}

#[component]
async fn hello(cx: &Cx, name: &str) -> Result<impl View> {
    Ok(view! { <h1>"Hello, " (name) "!"</h1> })
}

Topcoat 的不同之处#

无需样板代码的客户端响应性#

Topcoat 在服务器上渲染所有标记:组件可以是异步的,并直接查询数据库,从而消除了独立 API 层所需的全部传统样板代码。但交互性不必付出一次往返的代价。$(...) 表达式是普通的类型检查 Rust 代码,Topcoat 会在服务器上为初始渲染求值,也会把它翻译成 JavaScript,使其在浏览器中即时重新运行。不需要 wasm 包,也不需要客户端构建步骤:

#[component]
async fn faq(cx: &Cx) -> Result<impl View> {
    let open = signal(cx, || false);

    Ok(view! {
        // 完全在浏览器中运行;没有服务器往返。
        <button @click=$(|_e| open.set(!open.get()))>"What is Topcoat?"</button>
        <p :hidden=$(!open.get())>"A full-stack Rust framework."</p>
    })
}

当更新确实需要服务器时,例如获取新的搜索结果,把组件标记为 #[shard]。每当其某个 $(...) 参数发生变化,Topcoat 就会在服务器上重新渲染它,并把新 HTML 原地替换:

#[component]
async fn search(cx: &Cx) -> Result<impl View> {
    let query = signal(cx, String::new);

    Ok(view! {
        <input @input=$(|e: Event| query.set(e.target.value))>

        // 随着用户输入而更新。
        search_results(query: $(query.get()))
    })
}

#[shard]
async fn search_results(cx: &Cx, query: String) -> Result<impl View> {
    Ok(view! {
        <ul>
            // 你自己的服务器端代码,比如数据库查询:
            for product in search_products(cx, &query).await? {
                <li>(product.name)</li>
            }
        </ul>
    })
}

强大且符合直觉的 HTML 模板#

view! 宏忠于 HTML 和 Rust。在模板中使用熟悉的 Rust 控制流:

view! {
    <nav>
        for item in nav_items {
            <a
                href=(item.url)
                if item.url == current_path {
                    aria-current="page"
                    class="active"
                }
            >
                (item.label)
            </a>
        }
    </nav>
}

使用 topcoat fmt CLI 命令自动格式化代码库中的 view! 片段(及其他宏)。

基于模块的路由#

Topcoat 可选地从应用的模块结构推断路由树(无需构建步骤):

src/
|-- app.rs                  -> /(以及根 <html> 布局)
`-- app/
    |-- about.rs            -> /about
    |-- _marketing.rs       (布局,无 URL 片段)
    |-- _marketing/
    |   `-- pricing.rs      -> /pricing
    |-- posts.rs            -> /posts
    |-- posts/
    |   `-- id.rs           -> /posts/{post_id}
    `-- api/
        `-- health.rs       -> GET /api/health

可编辑的预制组件#

Topcoat UI 是一个基于 Tailwind 的组件库,灵感来自 shadcn/ui。组件通过 topcoat ui CLI 命令复制到你的项目中,这意味着你可以自由更改其设计和功能以适应你的用例:

#[component]
async fn delete_card(cx: &Cx) -> Result<impl View> {
    Ok(view! {
        card(
            card_header(
                card_title("Delete workspace")
                card_description(
                    "This permanently removes the workspace and all of its data."
                )
            )
            card_footer(
                attrs: attributes! { class="justify-end" },
                button(variant: ButtonVariant::Ghost, "Cancel")
                button(variant: ButtonVariant::Destructive, "Delete workspace")
            )
        )
    })
}

资源打包#

打包器会扫描编译后的二进制文件中的 asset! 调用,把每个文件复制(甚至下载)到本地资源目录,并让 Topcoat 以激进的浏览器缓存高效地提供这些资源。

const FERRIS: Asset = asset!("./ferris.png");

view! { <img src=(FERRIS)> }

Topcoat 还附带 Web 字体和图标工具,以及 Fontsource(Google Fonts)和 Iconify 的便捷集成。

内置 Tailwind 支持#

启用 tailwind 特性,把 Tailwind 集成到项目中:

view! { <link rel="stylesheet" href=(topcoat::tailwind::stylesheet!())> }

学习 Topcoat#

从这里开始#

  • 入门:使用 topcoat new 创建应用并运行开发服务器。
  • 手动设置:自行添加依赖并连接一个最小应用。
  • 源代码格式化:对宏体使用 topcoat fmt。

渲染#

  • view! 混合 HTML、Rust 表达式、控制流和组件调用。
  • #[component] 使异步函数可以带具名参数和子内容从标记中调用。
  • attributes! 收集属性,以传递给组件并展开到元素上。
  • class! 合并基础、可选和条件 CSS 类。
  • suspense 在内容加载时显示回退内容。error_boundary 在渲染失败时显示回退。
  • live! 和 emit! 在异步工作产生新 HTML 时替换页面的一部分。

路由#

  • Router:页面、布局和 API 路由;支持手动和自动发现。
  • 基于模块的路由:从模块树派生路由表。
  • 链接到处理器:使用 href! 生成导航链接、表单操作和重定向。

处理请求#

  • 上下文(Cx):读取请求数据和按类型存储的值。使用路由构建器的 .with 添加共享值,或使用 cx.with 为请求的一部分添加值。
  • 记忆化:使用 #[memoize] 进行每请求缓存和扇出去重。
  • Cookie:读写请求 cookie jar,支持签名、加密和带前缀的 cookie。
  • 会话:自带存储的会话认证:登录/登出生命周期、滑动过期和 token 轮换。

资源系统#

  • 资源:在 Rust 中声明资源,以内容哈希 URL 提供。
  • 字体:打包并提供 Web 字体。
  • 图标:下载 Iconify 图标集或声明自己的图标。

客户端响应性#

  • 运行时:信号、$(...) 表达式、@ 事件处理器和 : 绑定属性。
  • 表达式:双 Rust/JavaScript 表达式语言及其词汇表。
  • 过程(Procedures):可从浏览器调用的异步服务器函数。
  • 分片(Shards):参数变化时在服务器上重新渲染的组件。
  • 客户端导航:链接、预取以及无需整页重新加载的导航。

其他#

  • Topcoat UI:预制组件被复制到你的项目中供你编辑。
  • 元数据:为搜索引擎和社交网络设置每个布局和页面的标题、描述和链接预览。
  • 本地化:为每个请求选择语言,并显示 Fluent 文件中的翻译。
  • 邮件:使用 Mail::builder 构建邮件,通过 SMTP、文件或内存传输发送。
  • OpenAPI:在声明 API 路由的地方描述它们,并从路由器生成 OpenAPI 文档。

第三方集成#

  • Tailwind:无需 Node 的 Tailwind CSS,接入资源管道。
  • htmx:使用请求/响应头助手从服务器驱动部分 HTML 替换。
  • Alpine AJAX:使用 Alpine AJAX 的请求头约定从服务器驱动部分 HTML 替换。
  • Datastar:通过服务器发送事件把元素和信号补丁到页面中。

路线图#

我们希望为 Topcoat 带来的计划功能。有想法?打开 issue。

  • 静态导出
  • (更多)响应性(topcoat-runtime)
  • 更多 Topcoat UI 组件,完整“blocks”,例如登录表单
  • 更好的 Toasty 集成(从表单安全创建/更新记录,而无需列出所有字段)
  • 验证
  • 部署 Topcoat 的文档
  • 静态页面预渲染
  • WebTransport
  • 图像优化 / 缩放
  • Markdown 支持
  • 更易用的中间件,例如限流
  • 认证
  • 后台任务
  • Islands

关于#

一个开箱即用、用于构建 Web 应用的框架。

MIT 许可证。