Rust in Everything
基于 dioxus 0.7.10

响应式

这一部分讲 dioxus-signals、generational-box、dioxus-hooks 和 dioxus-stores。03 VirtualDom 内核讲到「某个 scope 被标脏之后怎么重跑」为止;这一部分回答前一半:值存在哪、谁读了它、写的时候通知谁。更新主线上「用户闭包写 signal → ReactiveContext::mark_dirty → SchedulerMsg」那一段,和最后的「effect」,都在这里。

整个响应式层只靠两样东西:generational-box 提供「可以随便复制、由 Owner 统一回收」的存储,dioxus-core 里的 ReactiveContext 记录「谁在读」。Signal 把两者接起来;memo、effect、resource 各自带一个 ReactiveContext;context 和全局 signal 解决「句柄怎么传到别的组件」;store 把一个大值拆成可以分别订阅的字段。

  1. generational-box:Signal 能 Copy,是因为句柄只是「格子地址 + 代数」;值放在泄漏出来、反复复用的格子里,Owner drop 时把代数加一,旧句柄再读得到 ValueDroppedError;UnsyncStorage 用 RefCell、冲突时报错,SyncStorage 用 RwLock、同线程冲突时死锁;每个 scope 在 context 表里放一个 Owner。
  2. Signal:Signal<T> 是 CopyValue<SignalData<T>>;read 订阅栈顶的 ReactiveContext,peek 不订阅;组件每次重跑先清空订阅,只留这一次读到的;读写冲突同一个句柄在编译期就报 E0502,复制出来的句柄在运行期 panic;signal()、+=、"{signal}" 各从哪来;渲染时先读后写会无限重跑,0.7 不再警告。
  3. Memo 与 effect:memo 依赖一变就由自己的任务重算,读的时候还脏就先同步重算,值相等不通知下游;effect 在 DOM 改完之后、按 scope 高度从父到子跑;普通 props 不是依赖,要用 use_reactive! 放进 signal。
  4. 异步:resource、future 与 spawn:use_resource 依赖变了就取消旧 future 重跑;use_future、use_coroutine、spawn;这些 future 由谁 poll、组件卸载时怎么取消。
  5. context 与全局 signal:use_context_provider / use_context;GlobalSignal 的值实际存在哪,SSR 时不同请求之间会不会串。
  6. Store:#[derive(Store)] 给每个字段生成可以单独订阅的投影,改一个字段只通知读这个字段的地方。
flowchart TB
  owner["Owner 每个 scope 一个"] --> gbox["generational-box 格子"]
  gbox --> copyv[CopyValue]
  copyv --> sig["Signal T"]
  rc[ReactiveContext] -->|订阅| sig
  sig -->|写锁释放 mark_dirty| rc
  rc --> comp["组件 重跑"]
  rc --> memo[Memo]
  rc --> effect[effect]
  rc --> res[resource]
  sig --> ctx["context 与全局 signal"]
  sig --> store[Store]

前三页是机制本身,后三页是用法上的扩展。ReactiveContext 标脏之后,组件怎么排队重跑见调度;任务怎么被 poll 见异步。

💬 关联讨论

发起讨论
加载中...