一棵树,多种宿主

跨平台共享组件。

使用 Widget、Scope、语义事件和类型化样式编写应用组件。将 DOM 查找、HTML 生成、窗口创建和事件循环放入小型的宿主专用入口中。

共享组件只写一次

这个组件不包含 web-sys、HTML 文档或窗口类型。无论挂载到哪里,其 Cage 和事件处理器的所有权与更新行为都保持一致。

shared.rs

1 2 3 4 5 6 7 8 9 10 11 12 13 14
use glory::prelude::*; pub fn counter() -> impl Widget { widget(|scope| { let count = scope.cage(0_u64); col().class("counter").fill(( text().content(count.map(|count| format!("Count: {count}"))), button() .label("Increment") .on(activate, move |_| count.revise(|value| *value += 1)), )) .show_in(scope) }) }

只转发使用的特性

Glory 没有默认宿主。应用特性转发到 facade 特性,使服务器、浏览器和原生依赖图保持显式。web 和 ssr 都选择 html;hydrate 选择 web;stream 选择 ssr 和 resources;blitz 仅适用于原生。渐进式浏览器客户端可用 stream-web,它选择 stream 和 hydrate(因此也包含 web);应在最后一个 End 帧后对应用执行水合。

Cargo.toml

1 2 3 4 5 6 7 8 9 10 11 12 13
[features] default = [] web = ["glory/web"] ssr = ["glory/ssr"] hydrate = ["glory/hydrate"] stream = ["glory/stream"] stream-web = ["glory/stream", "glory/hydrate"] blitz = ["glory/blitz"] desktop = ["glory/blitz-shell"] desktop-accessibility = ["glory/blitz-accesskit"] [dependencies] glory = { version = "0.3.1", default-features = false }

选择宿主入口

web_and_server.rs

1 2 3 4 5 6 7 8 9 10
// Native server: produce a versioned fragment for an otherwise empty mount. let snapshot = glory::ssr::snapshot(counter())?; let fragment = snapshot.html; // wasm32 browser CSR: mount into an empty element and retain the WebApp. let app = glory::web::launch(csr_root, counter())?; // wasm32 browser hydration: adopt the matching SSR fragment instead. // Use the same initial widget tree, and retain this WebApp as well. let app = glory::web::hydrate(hydration_root, counter())?;

launch 需要一个空且未被占用的浏览器挂载元素。hydrate 要求 Glory 的版本化 SSR 标记,并在接管前验证整个初始子树。服务器没有浏览器输入循环;交互始于浏览器宿主。

desktop.rs

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] fn main() -> Result<(), Box<dyn std::error::Error>> { use glory::blitz::shell::{Shell, WindowApp, WindowAttributes}; let app = WindowApp::new(); app.mount(counter())?; let mut shell = Shell::new()?; shell.add_window( app, WindowAttributes::default().with_title("Glory counter"), ); shell.run()?; Ok(()) } #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] fn main() {}

WindowApp 拥有共享 App 和原生文档。Shell 拥有桌面事件循环和窗口。该入口需启用 blitz-shell;blitz-accesskit 额外启用可选的桌面无障碍桥接。

能力矩阵

无界面 · 无需特性

App::memory() 为测试记录语义树和类型化补丁。Profile::RECORD 不执行布局,也没有物理输入、焦点、几何测量或平台动作驱动。

Web CSR · web

wasm32 DOM 渲染,支持浏览器输入、焦点、测量、滚动、资源和 RAF 调度。launch 创建新树;请维持 WebApp 的生命周期。

SSR · ssr

通过 render、render_document 和 snapshot 在原生环境中同步输出 HTML 字符串、文档和版本化快照。会记录事件兴趣,但 SSR 没有物理输入或几何测量。

水合 · hydrate

通过 hydrate 或 hydrate_with,在浏览器中接管兼容的 SSR 快照。它包含 web,在修改 DOM 前完成验证,并在不匹配时保留原挂载内容。

流式渲染 · stream

原生、按需拉取的 Shell/Patch/End 输出,具有请求资源和取消能力。添加 web 以使用经典的预编辑脚本,添加 hydrate 以最终接管应用;单独的流式输出不提供浏览器输入宿主。

Blitz 文档 · blitz

原生无界面的 CSS、盒模型和文本布局、测量及程序化滚动。可选 paint/raster 提供帧或 CPU 图像。独立 Renderer 没有操作系统输入驱动。

Blitz 桌面 · blitz-shell

WindowApp 和 Shell 在桌面目标上增加 CPU 桌面窗口、激活、焦点、导航、受控编辑和 IME 协议集成。AccessKit 需要单独启用。

共享语义不会消除平台差异

跨平台组件不保证像素完全相同。字体、文本整形、视口和 DPI 行为、CSS 覆盖范围及原生呈现均有差异。无界面测试通过既不能证明布局正确,也不能证明像素已显示;内存宿主和独立 Blitz Renderer 都不提供用户输入。

明确当前平台限制

桌面 Shell 仅支持 Windows、Linux 和 macOS,并不是 Android 宿主。Android Activity/Surface 集成、移动资源、输入连接和真机验收仍是独立工作。不能根据 Android 编译检查或桌面 winit 依赖推断移动端已受支持。

IME 协议处理不等于所有操作系统输入法均已验证。同样,可选 AccessKit 桥接和 Windows UI Automation 证据也不等于屏幕阅读器验收。Unix AT-SPI 客户端、语音播报、完整文本选择接口、操作系统输入法行为和移动无障碍仍需各平台验证。