界面里的“值变了”不是同一种动作。AUP 至少区分读取数据、写回数据、把数据投影到某个 prop、保存 UI-local state,以及处理用户事件。先选对合同,才能让 renderer、AFS scope 和测试知道谁负责什么。
四种数据/状态关系
| 形式 | 当前含义 | 不应推断 |
|---|---|---|
src | 只读 AFS 数据路径 | 自动拥有写入权限 |
bind | 常由 input 使用的读写 AFS binding | 任意 path 都允许读写 |
propBind | server-managed 的 dotted prop path → AFS path 只读绑定 | renderer 自己会访问 AFS 或管理订阅 |
state | UI-local 状态对象 | 所有 state 都必然 client-only 或会持久化 |
propBind 的初始值会在首次 render 前解析,源变化后会通过 patch 更新 props。输入 validation 是一个明确标注的 client-only 瞬时 state;其他 state 的生命周期应按它所在的 app/renderer 实际行为验收,不要用一个笼统假设代替测试。
当前 DSL 可以把 from "path" 编译为 src,也接受 bind=...、propBind={...}、state={...} 与 events={...}。路径校验会拒绝 javascript: 和 ..;这不是任意 AFS 路径自动获得访问授权的承诺。
事件的三类结果
一个 AUPEvent 可以:
- 用
exec和可选args调用 AFS action; - 用
target+set更新 node 的src、props、state或events; - 用
navigate做客户端浏览器级 URL 导航。
当前推荐的命名页导航是将 target 设为 _root,并写 set: { page: "name" }。旧 JSON 形式 page: "name" 已弃用,新的 DSL 则可写成 -> page details,由编译器生成推荐的 root/set 形状。
以下 DSL 片段来自当前 parser 的受理形状:
button open "Open" primary -> page details
button load "Load" primary -> set viewer src "/items/42"
button bump "Bump" primary -> set dial state {value: 7, dirty: true}
action -> exec "/.auth/logout"
action -> navigate "/.well-known/service/login"-> 是 button/action 的 click sugar;其他 node 应明确写 on <event> -> ...。同一次更新若同时包含 src 和 props 等复合 set,使用完整 events={...},不要期待单行 sugar 自动表达所有组合。
用可观察结果验收
先验证一件事:输入或 action 是否产生预期 patch/页面变化。然后验证 AFS action 是否只在正确的 caller scope 被调用。最后再看 renderer 的 loading、validation 或动画状态。把这三层混成“按钮能点”会掩盖读写权限、更新目标或导航语义的错误。
下一步阅读Session、patch 与 scene了解更新怎样在交互会话中传播。