ArkTS 进阶之道(9):@Provide/@Consume 跨层传值——为啥不叫全局变量

发布时间:2026/7/29 11:36:10
ArkTS 进阶之道(9):@Provide/@Consume 跨层传值——为啥不叫全局变量 ArkTS 进阶之道9Provide/Consume 跨层传值——为啥不叫全局变量本文是「ArkTS 进阶之道」系列第 9 篇续「ArkUI 状态哲学」阶段。上篇讲 Prop/Link 父子传值数据流绑定篇 57——父子两层数据流。本文讲跨层传值Provide/Consume 跨层接——根因在键值注册机制Provide 注册键值供跨层 Consume 接不用逐层透传。能力系列篇 15 讲过 Provide/Consume 怎么用本文讲为哈不叫全局变量——根因在键值注册不是全局作用域。一、开篇Provide/Consume 不是全局变量是键值注册的跨层绑定你写 TypeScript/React 时跨层传值是「魔法」React Context 顶层注册 孙层 useContext 接中间层不透传// React Context 跨层接 const SharedContext createContext(0) function Top() { const [value, setValue] useState(0) return SharedContext.Provider value{value}Mid //SharedContext.Provider ← 顶层注册 } function Mid() { return Grand / } ← 中间层不透传 function Grand() { const v useContext(SharedContext) ← 孙层跨层接顶层注册 return Text{v}/Text } // 孙层 useContext 跨层接顶层 Provider 注册值中间层不透传你写鸿蒙 ArkTS 时Provide/Consume 贬饰器键值注册跨层接——不用手写 Context Provider// ArkTS Provide/Consume 键值注册跨层接 Entry Component struct Top { Provide(sharedKey) shared: number 0 // 顶层 Provide 注册键值 build() { Column() { GrandLayer() } // 中间层不透传 } } Component struct GrandLayer { Consume(sharedKey) shared: number // 孙层 Consume 跨层接键值 build() { Text(${this.shared}) } } // 孙层 Consume(sharedKey) 跨层接顶层 Provide 注册的键值中间层不透传魔法 vs 注册的区别React 把跨层传值当「魔法」你手写 Context Provider 注册 useContext 接ArkTS 把 Provide/Consume 当「键值注册绑定」装饰器编译期注册键值跨层 Consume 按键接。根因不是魔法是注册——Provide 编译期注册键值供跨层 Consume 按键接不是全局作用域的变量。二、根因Provide/Consume 的键值注册跨层绑定机制鸿蒙 ArkUI 的 Provide/Consume 是键值注册跨层绑定——编译期 Provide 注册键值跨层 Consume 按键接不用逐层透传来自三重注册机制。机制 1Provide 注册键值——编译期在组件树注册键值Provide 贬饰器编译期注册键值——在组件树顶层注册一个键值供跨层 Consume 按键接Entry Component struct Top { Provide(sharedKey) shared: number 0 // Provide 注册键值 sharedKey build() { Column() { GrandLayer() // 孙层跨层 Consume(sharedKey) 接 } } }注册键值Provideshared贬饰器编译期在组件树注册键值sharedKey——不是全局作用域的变量是组件树内按键查找的注册项。赋值this.shared触发通知所有 Consume(‘sharedKey’) 的组件重渲。注册键值不用手写 Context Provider装饰器编译期注册。机制 2Consume 按键跨层接——编译期按键查找上层注册Consume 贬饰器编译期按键跨层接——跨层向上查 Provide 注册的同键值不用逐层透传Component struct MidLayer { build() { Column() { GrandLayer() } // 中间层不透传 Prop直接嵌孙层 } } Component struct GrandLayer { Consume(sharedKey) shared: number // Consume 按键跨层接顶层 Provide 注册 build() { Text(孙层 ${this.shared}) ← 依赖 Consume 跨层接的值 } }按键跨层接Consumeshared贬饰器编译期按键sharedKey向上查 Provide 注册的同键值——跨过中间层 MidLayer 直接接顶层 Top 的 Provide 注册值。不用中间层透传 PropConsume 按键跨层查找。根因不是全局变量是按键查找的注册绑定。机制 3Provide/Consume 追踪范围是组件树——跨层不逐层透传Provide/Consume 编译期绑的追踪范围是组件树——Provide 变通知所有 Consume 同键组件重渲跨层不逐层透传// Provide 追踪范围组件树内所有 Consume 同键组件 // 顶层 Provide shared 变 → 通知所有 Consume(sharedKey) 组件重渲跨层 // 中间层不用透传 PropConsume 按键直接跨层接 // 对比 Prop 追踪范围逐层透传 // 顶层 State 变 → Prop 传子 → 子 Prop 传孙 → 孙 Prop 刷逐层透传链 // 中间层要透传 Prop层数深时透传链冗长追踪范围区别Provide/Consume 编译期绑组件树范围——Provide 变通知所有 Consume 同键组件跨层重渲不用逐层透传。对比 Prop 逐层透传父传子、子传孙、孙传孙孙层数深时透传链冗长。根因不是全局变量是组件树范围的按键注册追踪。三、真机配图Provide/Consume 跨层接 vs Prop 逐层透传对比证据初始态顶层 Provide sharedValue1、顶层 State propSource1、孙层 Consume 接1、孙层 Prop 透传1 均初始值点调改 Provide 改 State 后Provide sharedValue2 跨层 Consume 孙刷不透传、State propSource2 逐层 Prop 透传刷均对比证据齐对比证据改 ProvidesharedValue后跨层 Consume 孙层直接刷中间层不透传改 StatepropSource后 Prop 逐层透传刷中间层要透传。Provide/Consume 不是全局变量是键值注册的跨层绑定——Provide 注册键值供跨层 Consume 按键接不用逐层透传根因在按键查找的注册绑定不是全局作用域。四、真解法Provide/Consume 用法的三个场景场景 1跨层共享状态孙层接顶层不逐层透传深嵌套首选Entry Component struct Top { Provide(theme) theme: string light // Provide 注册主题键值 Provide(lang) lang: string zh // Provide 注册语言键值 build() { Column() { MidLayer() } // 中间层不透传孙层 Consume 跨层接 } } Component struct MidLayer { build() { Column() { GrandLayer() } } // 中间层不透传 Prop } Component struct GrandLayer { Consume(theme) theme: string // 孙层 Consume 跨层接顶层主题 Consume(lang) lang: string // 孙层 Consume 跨层接顶层语言 build() { Column() { Text(主题${this.theme}) Text(语言${this.lang}) } } }为哈能跑Provide/Consume 跨层共享——孙层 Consume 跨层接顶层 Provide 注册值中间层不透传。深嵌套首选这个层数深时 Prop 逐层透传链冗长Provide/Consume 跨层接清爽。要写「顶层管全局配置 孙层跨层接」时用这个——不用逐层透传 PropConsume 按键跨层接。场景 2多孙层同键接多个孙层接同个 Provide一键多接Entry Component struct Top { Provide(user) userName: string 游客 // Provide 注册用户键值 build() { Column() { HeaderLayer() // 多个孙层都 Consume(user) 接同键 ContentLayer() FooterLayer() } } } Component struct HeaderLayer { Consume(user) userName: string // 孙层 1 Consume 接用户键值 build() { Text(头部${this.userName}) } } Component struct ContentLayer { Consume(user) userName: string // 孙层 2 Consume 接同键值 build() { Text(内容${this.userName}) } } Component struct FooterLayer { Consume(user) userName: string // 孙层 3 Consume 接同键值 build() { Text(底部${this.userName}) } }为哈能跑Provide 注册一个键值多个 Consume 同键组件都跨层接——顶层 ProvideuserName变通知所有 Consume(‘user’) 的孙层重渲。要写「顶层管用户状态 多个孙层都接」时用这个——一键多接Provide 变所有 Consume 同键组件刷不用逐个 Prop 透传。场景 3Provide/Consume vs Prop/Link 选型跨层深 vs 父子近// 跨层深3 层以上用 Provide/Consume不逐层透传 Entry Component struct Top { Provide(data) data: number 0 // 跨层 3 层以上用 Provide build() { Column() { Mid1() } } } struct Mid1 { build() { Mid2() } } // 中间层不透传 struct Mid2 { build() { GrandLayer() } } struct GrandLayer { Consume(data) data: number // 孙层跨层接不透传 build() { Text(${this.data}) } } // 父子近2 层用 Prop/Link直接父子传 Entry Component struct Parent { State value: number 0 // 父子 2 层用 Prop/Link build() { Column() { Child({ v: this.value }) } } } struct Child { Prop v: number // 父子近用 Prop 直接传 build() { Text(${this.v}) } }为哈能跑Provide/Consume vs Prop/Link 选型按嵌套深度——跨层深3 层以上用 Provide/Consume 不逐层透传父子近2 层用 Prop/Link 直接传。要写「嵌套深的组件树共享状态」时用 Provide/Consume要写「父子两层直接传」时用 Prop/Link——按嵌套深度选型跨层深透传链冗长用注册父子近直接传用绑定。五、一句话哲学Provide/Consume 不是全局变量是键值注册的跨层绑定。ArkUI 的 Provide 贬饰器编译期在组件树注册键值供跨层 Consume 按键接Consume 贬饰器编译期按键向上查 Provide 注册的同键值跨层接不用逐层透传。根因不是全局作用域的变量是按键查找的注册绑定——Provide 注册键值组件树注册 Consume 按键跨层接向上查同键 追踪范围组件树跨层不逐层透传。对比 Prop 逐层透传链冗长Provide/Consume 跨层接清爽。状态哲学阶段串讲State 单组件内部篇 56赋值就刷 UI 依赖追踪→ Prop/Link 父子传值篇 57单向 vs 双向数据流绑定→ Provide/Consume 跨层传值篇 58键值注册跨层绑定——从「单组件内部」到「父子两层」到「跨层多层」扩展根因都是编译期绑追踪范围。为啥不叫全局变量全局变量是全局作用域任意处可访问Provide/Consume 是组件树内按键注册查找——作用域不是全局是组件树查找不是任意是按键绑定不是作用域是注册。叫键值注册的跨层绑定不是全局变量根因在按键查找的注册绑定机制。下一篇ArkTS 进阶之道10—— Watch 状态监听从回调时机理解响应式边界对应能力系列篇 16讲根因——续「ArkUI 状态哲学」阶段收尾。能力系列回链能力系列篇本文进阶点篇 15 Provide/Consume 用法键值注册跨层绑定根因篇 14 Prop/Link 用法上一篇单向 vs 双向数据流绑定篇 16 Watch 用法下篇预告状态监听回调时机根因真机 demo 完整代码// 篇 58 demoProvide/Consume 跨层传值 vs Prop 逐层透传对比 // 用 V1 贬饰器Component 里用避 V2 双轨坑 // 中间层组件Prop 逐层透传对比证据 Component struct MidPropLayer { Prop passed: number // Prop 逐层透传父传中、中传孙 build() { Column({ space: 6 }) { Text(中间层 Prop ${this.passed}).fontSize(13).fontColor(#888) // 孙层接 Prop 透传值 Text(孙层接透传 ${this.passed}).fontSize(14).fontColor(#2563eb) } .alignItems(HorizontalAlign.Center) } } // 中间层组件Consume 直接接不透传 Component struct MidConsumeLayer { Consume(sharedKey) shared: number // Consume 直接接上层 Provide不透传 build() { Column({ space: 6 }) { Text(中间层 Consume ${this.shared}).fontSize(13).fontColor(#dc2626) // 孙层也 Consume 直接接不透传 GrandConsumeLayer() } .alignItems(HorizontalAlign.Center) } } // 孙层组件Consume 直接接跨层不透传 Component struct GrandConsumeLayer { Consume(sharedKey) shared: number // 孙层 Consume 跨层直接接顶层 Provide build() { Text(孙层 Consume 跨层接 ${this.shared}).fontSize(14).fontColor(#dc2626) } } Entry Component struct Index { // ✅ Provide 贬饰器跨层提供不逐层透传 Provide(sharedKey) sharedValue: number 1 // Prop 透传用的源 State propSource: number 1 State log: string (未操作) build() { Column({ space: 12 }) { Text(篇 58 配图Provide/Consume 跨层传值 vs Prop 逐层透传) .fontSize(18).fontWeight(FontWeight.Bold).margin({ top: 20, bottom: 8 }) Text(Consume 跨层直接接 vs Prop 逐层透传对比证据) .fontSize(12).fontColor(#888).margin({ bottom: 16 }) Column({ space: 6 }) { Text(顶层 Provide sharedValue ${this.sharedValue}).fontSize(15).fontWeight(FontWeight.Bold) Text(顶层 State propSource ${this.propSource}).fontSize(15).fontWeight(FontWeight.Bold) Text(日志${this.log}).fontSize(12).fontColor(#333).margin({ top: 4 }) } .width(92%).padding(12).backgroundColor(#f5f5f5).borderRadius(8) // Prop 逐层透传顶层 → 中间层 → 孙层要逐层传 Text(Prop 逐层透传链路).fontSize(13).fontColor(#2563eb) MidPropLayer({ passed: this.propSource }) // Provide/Consume 跨层接顶层 Provide → 孙层 Consume不透传 Text(Provide/Consume 跨层接链路).fontSize(13).fontColor(#dc2626) MidConsumeLayer() Button(改 Provide sharedValue跨层孙刷) .width(92%).height(44).fontSize(14) .onClick(() { this.sharedValue // 改 Provide跨层 Consume 孙刷不透传 this.log 改 sharedValue${this.sharedValue}跨层 Consume 孙刷不透传 }) Button(改 State propSource逐层透传刷) .width(92%).height(44).fontSize(14) .onClick(() { this.propSource // 改 StateProp 逐层透传刷 this.log 改 propSource${this.propSource}Prop 逐层透传刷 }) } .width(100%).height(100%).alignItems(HorizontalAlign.Center) } }写鸿蒙 ArkUI 记住Provide/Consume 不是全局变量是键值注册的跨层绑定——Provide 贬饰器编译期在组件树注册键值供跨层 Consume 按键接Consume 贬饰器编译期按键向上查 Provide 注册的同键值跨层接不用逐层透传。根因不是全局作用域的变量是按键查找的注册绑定——作用域是组件树不是全局查找是按键不是任意绑定是注册不是作用域。跨层深3 层以上用 Provide/Consume 不逐层透传深嵌套首选父子近2 层用 Prop/Link 直接传一键多接用 Provide 多孙层 Consume 同键。按键注册跨层接不逐层透传是 ArkUI 状态哲学核心