Rust in Everything
基于 topcoat 0.10.0

topcoat 源码解析

全卷只对着一个版本写:

crate版本tagcommit日期
topcoat 及工作区里所有发布的子 crate0.10.0v0.10.03e7bb432026-10-03

topcoat 是 tokio 组织下的全栈 Web 框架,README 开头用粗体写着「Early-stage and experimental. Expect breaking changes.」本卷只对 0.10.0 负责:tag 之后两周里上游又合了 28 个提交,其中有两处正好改到 02 router 讲的代码(给 hyper 配计时器、限定模块路由的发现范围),讲到时会注明。

它和前几卷的关系有点特别:topcoat 不依赖 axum。服务器直接用 hyper 1 和 hyper-util 的 auto::Builder(hyper 卷),路径匹配直接用 matchit 0.9,tower 只在压缩和「与 tower 互操作」两处出现(tower 卷)。所以读它的路由,等于看另一种在 hyper 之上搭框架的办法,和 axum 卷可以逐处对照。

全卷沿三条线展开:

  • 请求主线(服务端 → HTML):topcoat::start 绑端口,accept 循环给每个连接起一个任务;Router 先用 catch_unwind 把整个请求包起来,再给请求组一个按类型存值的上下文 Cx,用 matchit 找端点、按方法挑路由,外面永远先过一层 Origin 检查;#[page] 渲染出的视图先发首屏,suspense 里还没完成的部分以 <template> 加一小段脚本追加在同一个分块响应里。
  • 发现主线(启动时,路由从哪来):没有 build.rs,也不扫文件系统。#[page] 等属性宏展开时用 inventory 在链接期登记自己,并记下 module_path!();module_router!() 在启动时把这些登记项取出来,按模块路径切段,推出 URL。
  • 交互主线(浏览器 → 服务端 → 局部 HTML):$(...) 表达式同时生成服务端求值的 Rust 和写进 HTML 属性的 JavaScript;只读浏览器端状态的表达式就地重算,需要服务端的部分(shard)通过 WebSocket 或 HTTP 请求重新渲染,新 HTML 被 morph 进页面。
flowchart TB
  intro[00 导读] --> overview[01 全景]
  overview --> router[02 router]
  router --> cx[03 上下文]
  cx --> view[04 view]
  view --> rt[05 浏览器运行时]
  router --> assets[06 资产与工具链]
  router --> sec[07 安全默认值]
  rt --> integ[08 第三方交互集成]
  rt --> vs[09 与本站方案对比]
  vs --> design[10 设计取舍]
  design --> pit[11 常见坑]
  pit --> site[12 在本站中]
  site --> hands[13 动手]

实验环境

文中每个示例都实际跑过一次:Apple M2 Max、macOS、rustc 1.99.0,依赖锁在 topcoat =0.10.0(full feature)、tokio =1.54.1,解析出来的是 hyper 1.12.0、hyper-util 0.1.21、matchit 0.9.2、tower 0.5.3、tower-http 0.7.1。上游自己的 Cargo.lock 用的是 tokio 1.51.1、hyper 1.9.0,版本不同的地方只影响行为细节,不影响本卷讲的 topcoat 代码。服务器都绑在 127.0.0.1,用 curl 或一个打印每次 read 时间的小客户端发请求。

各部分

  1. 00 导读:锁定的版本、三条主线、阅读路线和前置知识;17 个 crate 的分层,X / X-grammar / X-macro 三件套,门面 crate 的 feature。
  2. 01 全景:topcoat 想解决什么问题,和同类方案的位置;一个带 suspense 的页面从监听到 HTML 的全过程,逐段标注分块响应。
  3. 02 router:本卷重点。连接怎么接进来、请求怎么分发;路由表和 layer 链;模块路由与链接期发现;请求、响应与「错误即控制流」;tower 互操作、可信代理与压缩。
  4. 03 上下文:Cx 是一张按类型存值的表,#[memoize],以及官方「函数,而不是中间件」的建议。
  5. 04 view:view! 展开成什么;组件、同一任务里的并发渲染与流式。
  6. 05 浏览器运行时:本卷另一个重点。$(...) 一份 Rust 两种执行;signal、shard 与 procedure。
  7. 06 资产与工具链:asset! 与二进制里的字节标记;tailwind、font、icon 与 topcoat CLI。
  8. 07 安全默认值:Origin 检查、cookie 与 session。
  9. 08 第三方交互集成:htmx、Datastar、Alpine AJAX 都是读写请求头的薄层,片段与整页共用 URL 时缺的 Vary;顺带 mail!。
  10. 09 与本站方案对比:topcoat 与 Dioxus fullstack,按源码事实和实测逐项比,写各自适合什么。
  11. 10 设计取舍:把各章的取舍汇总到一起,附 0.10.0 的已知问题表。
  12. 11 常见坑:用报错原文或现象做标题,每个都先跑出最小复现。
  13. 12 在本站中:本站没有用 topcoat,这一章是「把本站一页用 topcoat 写会怎样」的对照。
  14. 13 动手:用 hyper 和 matchit 写一个迷你版的思路。

从「导读」开始。只想会用 topcoat 的读者,读 01、02 的模块路由、04、05、07、11;想读懂实现的,按顺序读。

💬 关联讨论

发起讨论
加载中...