响应式
这一部分讲 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 把一个大值拆成可以分别订阅的字段。
- generational-box:
Signal能Copy,是因为句柄只是「格子地址 + 代数」;值放在泄漏出来、反复复用的格子里,Ownerdrop 时把代数加一,旧句柄再读得到ValueDroppedError;UnsyncStorage用RefCell、冲突时报错,SyncStorage用RwLock、同线程冲突时死锁;每个 scope 在 context 表里放一个Owner。 - Signal:
Signal<T>是CopyValue<SignalData<T>>;read订阅栈顶的ReactiveContext,peek不订阅;组件每次重跑先清空订阅,只留这一次读到的;读写冲突同一个句柄在编译期就报 E0502,复制出来的句柄在运行期 panic;signal()、+=、"{signal}"各从哪来;渲染时先读后写会无限重跑,0.7 不再警告。 - Memo 与 effect:memo 依赖一变就由自己的任务重算,读的时候还脏就先同步重算,值相等不通知下游;effect 在 DOM 改完之后、按 scope 高度从父到子跑;普通 props 不是依赖,要用
use_reactive!放进 signal。 - 异步:resource、future 与 spawn:
use_resource依赖变了就取消旧 future 重跑;use_future、use_coroutine、spawn;这些 future 由谁 poll、组件卸载时怎么取消。 - context 与全局 signal:
use_context_provider/use_context;GlobalSignal的值实际存在哪,SSR 时不同请求之间会不会串。 - 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 见异步。
💬 关联讨论
发起讨论加载中...