菜单 · 文档

最后更新:2026年8月12日

文档

需要跨多层传递依赖时,Provider 该怎么放、谁来持有。

上下文与 Provider

需要把某个依赖穿透多层组件时,用 Context 比一层层传 props 干净。Provider 挂在 subtree 根上,下面的组件取最近一层同类型值。

定义上下文

#[derive(Clone, Copy)]
struct SessionContext {
    user_id: Signal<Option<u64>>,
}

#[component]
fn SessionProvider(children: Element) -> Element {
    let user_id = use_signal(|| None);
    use_context_provider(|| SessionContext { user_id });
    rsx! { {children} }
}

#[component]
fn ProfileButton() -> Element {
    let session = consume_context::<SessionContext>();
    rsx! { button { "user = {session.user_id:?}" } }
}

Context 类型应表达领域职责。不要用一个巨大 AppContext 包含所有可变状态,否则任何模块都会隐式依赖整个应用。

Provider 所有权

Provider 负责:

  • 创建状态或外部资源。
  • 向 subtree 发布稳定 handle。
  • 在 scope 卸载时取消订阅、任务和 native 资源。
  • 定义没有 provider 时是 panic、错误还是默认值。

Arkit 自带的 root-local RuntimeHandle、WindowMetrics、I18n、Router、Theme 和 AnimationHost 都遵循这一模型。原生节点不通过 Context 猜测,而用显式 NativeElementRef 绑定。

何时不用 Context

只有一两层传递时直接用 Props,依赖关系更清晰。列表 item 的当前数据也应通过 Props,而不是循环里覆盖同类型 Context。

响应式粒度

Context 本身通常保持稳定,内部使用多个 Signal 划分更新粒度。consumer 只读取需要的 Signal;把每次变化都重新构造为一个大结构会扩大重渲染范围。

嵌套覆盖

测试、预览或子路由可以安装同类型 Provider 覆盖上层。例如某个设置页临时预览另一主题。consumer 总是读取最近的 provider,卸载后自然回到外层值。