Rust in Everything
基于 dioxus 0.7.10

VirtualDom 内核

这一部分讲 dioxus-core,整个框架里和平台无关的那一层。源码在 packages/core/src(约 1 万行),这一部分要读的是 virtual_dom.rs、runtime.rs、scope_arena.rs、scope_context.rs、scopes.rs、diff/{node,component,iterator}.rs、arena.rs、mutations.rs、scheduler.rs、tasks.rs、events.rs、suspense/、error_boundary.rs。浏览器、服务端、桌面都只是给它喂事件、收指令:VirtualDom 不知道 DOM 长什么样,它只产出一串 WriteMutations 调用,由渲染器去执行。

02 rsx 与组件讲到 rsx! 产出 VNode 为止;这一部分从「组件函数被谁调用」讲起,到「改 DOM 的指令怎么发出去」为止。更新主线中间的那一大段(mark_dirty → render_immediate → run_scope → diff → WriteMutations)都在这里。

  1. VirtualDom 与 Runtime:VirtualDom 拿着组件树(scopes)、脏队列和调度通道的接收端,Runtime 拿着其余所有共享状态,Rc 共享、用线程局部的栈表示「当前 runtime」;rebuild、wait_for_work、render_immediate、wait_for_suspense 几个入口怎么被 web 主循环和 SSR 组合起来。
  2. Scope 与 hooks:每个组件实例一个 ScopeId,用户组件从 3 号开始;run_scope 在组件自己的 ReactiveContext 里调用组件函数;use_hook 只按调用序号存取,hook 放进条件里时类型不同就 panic、类型相同就静默串值;context 沿父链查找;卸载时先子后父,hook 倒序 drop。
  3. diff:同一个模板只比动态节点和动态属性,静态部分不参与比较;模板不同就整块替换;文本、属性、组件、Fragment、占位符各自怎么比;组件靠 memoize 决定重不重跑;ElementId 从 slab 里分配,删掉的 id 马上被下一个节点复用。
  4. 带 key 的列表:不带 key 时按位置配对,中间插一项会让后面每一项都被改写,组件状态跟着位置走、错位;带 key 时先剥两端,中间用最长递增子序列找出不动的节点,只移动其余的。
  5. Mutations:WriteMutations 的每个方法,指令是一台栈机器,模板整块克隆再按路径填动态部分;Mutations 和 NoOpMutations。
  6. 调度:scope、任务与 effect:脏 scope 和被唤醒的任务按高度从根往下交错执行,effect 等它们都做完才跑;SchedulerMsg;同一轮写多次 signal 只重跑一次。
  7. 事件:Runtime::handle_event 由 ElementId 找到挂载点,沿模板和父挂载点冒泡找监听器;stop_propagation、prevent_default。
  8. Suspense 与错误边界:组件 ? 出 RenderError::Suspended 或错误之后,最近的边界怎么接住;SSR 里怎么用这两个。
flowchart TB
  rsx["rsx 产出 VNode"] --> runscope["run_scope 跑组件"]
  runscope --> scope["Scope 与 hooks"]
  runscope --> diffnode["diff 新旧 VNode"]
  diffnode --> keyed["带 key 的列表"]
  diffnode --> muts["WriteMutations 指令"]
  muts --> renderer["渲染器改 DOM"]
  renderer --> evt["事件 handle_event"]
  evt --> sched["调度 脏 scope 队列"]
  sched --> runscope
  runscope --> susp["Suspense 与错误边界"]

前四页(VirtualDom 本身、scope、diff、带 key 的列表)讲「一次重渲染里发生什么」,后四页讲「重渲染由谁触发、结果怎么送出去、挂起和出错时怎么办」。signal 怎么记录订阅、写入时怎么通知组件,在 04 响应式;WriteMutations 在浏览器里的实现在 WebsysDom 与 interpreter,在服务端则不用指令,dioxus-ssr 直接沿 VNode 树写 HTML。

💬 关联讨论

发起讨论
加载中...