ArkTS 进阶之道(20):@Link 跨组件双向同步边界——父改子同步+子改父同步为啥要 $ 语法

ArkTS 进阶之道(20):@Link 跨组件双向同步边界——父改子同步+子改父同步为啥要 $ 语法
ArkTS 进阶之道20Link 跨组件双向同步边界——父改子同步子改父同步为啥要 $ 语法本文是「ArkTS 进阶之道」系列第 20 篇续「ArkUI 状态联动」深水区篇 68 开篇。上篇讲 Watch 状态监听边界Watch 绑 State 变触发副作用回调槽。本文讲跨组件双向同步边界Link 荬饰器父改子同步子改父同步双向——根因在 $ 语法双向绑定不是单向传值Link 父改子同步子改父同步双向Prop 父改子同步单向子改父不同步。能力系列篇 20 讲过 Link 怎么用本文讲为哈 Link 双向同步合法 Prop 单向同步子改父不同步——根因在 $ 语法双向绑定。一、开篇Link 不是单向传值是 $ 语法双向绑定的跨组件同步你写 React 时父子组件状态同步是「回调魔法」父传 props 子改父调回调// React 父子组件状态同步父传 props 子改父调回调 function Parent() { const [count, setCount] useState(0) return Child count{count} onCountChange{setCount} / // 父传 props 回调 } function Child({ count, onCountChange }) { return button onClick{() onCountChange(count 1)}子改 count{count}/button } // React 父子同步父传 props 子改父调回调回调魔法你写鸿蒙 ArkTS 时Link$ 语法双向绑定——父改子同步子改父同步双向// ArkTS Link $ 语法双向绑定 Entry Component struct Index { State linkCount: number 0 // ✅ 父 State linkCount build() { Column() { ChildLink({ childCount: $linkCount }) // ✅ $ 语法双向绑定父改子同步子改父同步 } } } Component struct ChildLink { Link childCount: number // ✅ Link 子 childCount 双向同步父 State linkCount build() { Button(子改 childCount${this.childCount}) .onClick(() { this.childCount }) // ✅ 子改 childCount → 父 State linkCount 同步 } } // Link $ 语法双向绑定父改子同步子改父同步双向回调魔法 vs $ 语法双向绑定的区别React 把父子同步当回调魔法父传 props 子改父调回调ArkTS 把 Link 当「$ 语法双向绑定荬饰器」父改子同步子改父同步双向。根因不是回调魔法是 $ 语法双向绑定——Link $ 语法编译期绑父子双向同步槽父改子同步子改父同步双向。二、根因Link 的 $ 语法双向绑定机制鸿蒙 ArkUI 的 Link 是**$ 语法双向绑定**——编译期把 Link 绑成父子双向同步槽父改子同步子改父同步双向来自三重绑定机制。机制 1Link $ 语法编译期绑父子双向同步槽——不是单向传值Link 荬饰器 $ 语法编译期绑父子双向同步槽——把 $linkCount 绑成父子双向同步槽父改子同步子改父同步双向Entry Component struct Index { State linkCount: number 0 // ✅ 父 State linkCount build() { Column() { ChildLink({ childCount: $linkCount }) // ✅ $ 语法双向绑定编译期绑父子双向同步槽 } } } Component struct ChildLink { Link childCount: number // ✅ Link 子 childCount 双向同步父 State linkCount build() { Column() { Text(childCount ${this.childCount}) Button(子改 childCountLink 子改父同步) .onClick(() { this.childCount }) // ✅ 子改 childCount → 父 State linkCount 同步 } } } // 编译期$linkCount 绑成父子双向同步槽父 State linkCount ↔ 子 Link childCount // 运行时父改 linkCount → 子 childCount 同步 子改 childCount → 父 linkCount 同步双向编译期绑父子双向同步槽Link $ 语法编译期把$linkCount绑成父子双向同步槽——父State linkCount↔ 子Link childCount双向同步。父改linkCount→ 子childCount同步子改childCount→ 父linkCount同步双向。根因不是单向传值是 $ 语法双向绑定。机制 2Prop 单向同步——父改子同步子改父不同步无 $ 语法Prop 荬饰器单向同步——父改子同步子改父不同步无 $ 语法子只改本地副本Entry Component struct Index { State propCount: number 0 // ⚠ 父 State propCount build() { Column() { ChildProp({ childCount: this.propCount }) // ⚠ Prop 单向同步无 $ 语法传值副本 } } } Component struct ChildProp { Prop childCount: number // ⚠ Prop 单向同步父改 childCount 子同步 子改 childCount 父不同步 build() { Column() { Text(childCount ${this.childCount}) Button(子改 childCountProp 子改父不同步) .onClick(() { this.childCount }) // ⚠ Prop 子改 childCount → 父 State childCount 不同步只改子本地副本 } } } // Prop 单向同步父改 propCount → 子 childCount 同步 子改 childCount → 父 propCount 不同步只改子本地副本Prop 单向同步Prop 无 $ 语法单向同步——父改propCount→ 子childCount同步子改childCount→ 父propCount不同步只改子本地副本。根因不是双向同步是单向传值副本——Prop 子只改本地副本不回传父。机制 3Link $ 语法 vs Prop 传值边界——双向同步 vs 单向传值Link$ 语法双向同步父改子同步子改父同步双向Prop传值单向同步父改子同步子改父不同步单向——根因都是父子同步但绑的机制不同Entry Component struct Index { State linkCount: number 0 // ✅ Link 父状态父改子同步 子改父同步双向 State propCount: number 0 // ⚠ Prop 父状态父改子同步单向子改父不同步 build() { Column() { // ✅ Link 路径$ 语法双向绑定父改子同步子改父同步双向 ChildLink({ childCount: $linkCount }) // ⚠ Prop 路径传值单向同步父改子同步子改父不同步单向 ChildProp({ childCount: this.propCount }) } } } // Link $ 语法双向绑定父改 linkCount → 子 childCount 同步 子改 childCount → 父 linkCount 同步双向 // Prop 传值单向同步父改 propCount → 子 childCount 同步 子改 childCount → 父 propCount 不同步单向Link $ 语法 vs Prop 传值边界Link 路径——$ 语法双向绑定父改子同步子改父同步双向推荐。Prop 路径——传值单向同步父改子同步子改父不同步单向子改只改本地副本。根因都是父子同步但绑的机制不同——Link $ 语法双向绑定双向同步槽Prop 传值单向同步单向传值副本。机制 4Link $ 语法必须配 State——不能配普通变量Link$ 语法必须配 State——$ 语法双向绑定要求父状态是 State响应式不能配普通变量Entry Component struct Index { State linkCount: number 0 // ✅ State 配 Link $ 语法双向绑定 private normalCount: number 0 // ❌ 普通变量不能配 Link $ 语法无响应式 build() { Column() { // ✅ Link $ 语法配 State双向同步合法 ChildLink({ childCount: $linkCount }) // ❌ Link $ 语法配普通变量编译报错$ 语法要求 State 响应式 // ChildLink({ childCount: $normalCount }) // ❌ 编译报错 } } } // Link $ 语法必须配 State$ 语法双向绑定要求父状态是 State响应式 // 普通变量无响应式不能配 Link $ 语法编译报错Link $ 语法必须配 StateLink $ 语法双向绑定要求父状态是 State响应式普通变量无响应式不能配 Link $ 语法编译报错。根因不是任意变量是 State 响应式——Link $ 语法双向同步槽要求父 State 响应式才能双向同步。三、真机配图Link 跨组件双向同步边界——$ 语法双向绑定初始态父 linkCount0、父 propCount0、两子组件 childCount0 均双向同步边界初始值点调四按钮后父 linkCount1 子 ChildLink childCount1 父子都同步、父 propCount1 子 ChildProp childCount1 父同步但子改后父 propCount 仍1 不同步均双向同步边界对比证据齐对比证据点父改 linkCount 后子 ChildLink childCount 同步父改子同步点子改 ChildLink childCount 后父 linkCount 同步子改父同步双向。点父改 propCount 后子 ChildProp childCount 同步父改子同步点子改 ChildProp childCount 后父 propCount 仍1 不同步子改父不同步单向。Link $ 语法双向绑定父改子同步子改父同步双向vs Prop 传值单向同步父改子同步子改父不同步单向——Link 荬饰器 $ 语法编译期绑父子双向同步槽Prop 传值单向同步子改只改本地副本。四、真解法Link 跨组件双向同步的三个场景场景 1Link 父子计数器双向同步90% 场景首选$ 语法双向绑定Entry Component struct Index { State count: number 0 // ✅ 父 State count build() { Column() { Text(父 count ${this.count}) ChildCounter({ childCount: $count }) // ✅ $ 语法双向绑定 } } } Component struct ChildCounter { Link childCount: number // ✅ Link 子 childCount 双向同步父 State count build() { Column() { Text(子 childCount ${this.childCount}) Button(子改 childCount) .onClick(() { this.childCount }) // ✅ 子改 childCount → 父 State count 同步 } } }为哈能跑Link 父子计数器双向同步——$ 语法双向绑定父改 count → 子 childCount 同步 子改 childCount → 父 count 同步双向。首选这个90% 的场景父子组件状态双向同步用 Link $ 语法双向绑定就够。要写「父子组件状态双向同步计数器等」时用这个——不用 Prop 单向同步子改父不同步Link $ 语法双向绑定父子都同步。场景 2Link 父子表单双向同步$ 语法双向绑定子改父同步Entry Component struct Index { State name: string // ✅ 父 State name build() { Column() { Text(父 name ${this.name}) ChildInput({ childName: $name }) // ✅ $ 语法双向绑定 } } } Component struct ChildInput { Link childName: string // ✅ Link 子 childName 双向同步父 State name build() { TextInput({ text: this.childName, placeholder: 输入姓名 }) .onChange((value: string) { this.childName value }) // ✅ 子改 childName → 父 State name 同步 } } // Link 父子表单双向同步子 Input 改 childName → 父 State name 同步子改父同步双向 // 不用 Prop 单向同步子改父不同步Link $ 语法双向绑定父子都同步为哈能跑Link 父子表单双向同步——$ 语法双向绑定子 Input 改childName→ 父State name同步子改父同步双向。要写「父子表单双向同步Input 改子父同步等」时用这个——不用 Prop 单向同步子改父不同步Link $ 语法双向绑定父子都同步。场景 3Link 父子对象双向同步$ 语法双向绑定对象引用同步interface User { name: string; age: number } Entry Component struct Index { State user: User { name: 张三, age: 25 } // ✅ 父 State user 对象 build() { Column() { Text(父 user ${JSON.stringify(this.user)}) ChildUser({ childUser: $user }) // ✅ $ 语法双向绑定对象 } } } Component struct ChildUser { Link childUser: User // ✅ Link 子 childUser 双向同步父 State user 对象 build() { Column() { TextInput({ text: this.childUser.name, placeholder: 姓名 }) .onChange((value: string) { this.childUser.name value }) // ✅ 子改 childUser.name → 父 State user.name 同步 Button(子改 age) .onClick(() { this.childUser.age }) // ✅ 子改 childUser.age → 父 State user.age 同步 } } } // Link 父子对象双向同步$ 语法双向绑定对象引用子改 childUser.name/age → 父 State user.name/age 同步双向 // 对象引用同步Link 绑对象引用子改对象属性父同步不用深拷贝为哈能跑Link 父子对象双向同步——$ 语法双向绑定对象引用子改childUser.name/age→ 父State user.name/age同步双向。要写「父子对象双向同步对象属性改父子都同步等」时用这个——不用 Prop 单向同步子改对象属性父不同步Link $ 语法双向绑定对象引用父子都同步。五、一句话哲学Link 不是单向传值是 $ 语法双向绑定的跨组件同步。ArkUI 的 Link 荬饰器 $ 语法编译期绑父子双向同步槽父改子同步子改父同步双向。根因不是单向传值是 $ 语法双向绑定——Link $ 语法编译期绑父子双向同步槽父 State ↔ 子 Link 双向同步 Prop 传值单向同步父改子同步子改父不同步子改只改本地副本 Link $ 语法必须配 State$ 语法双向绑定要求父 State 响应式 Link $ 语法 vs Prop 传值边界双向同步 vs 单向传值。对比 React 父子组件状态同步回调魔法父传 props 子改父调回调ArkTS Link $ 语法双向绑定父子都同步。状态联动深水区串讲Watch 绑 State 变触发副作用回调槽篇 68onClick 改 State 不直接调副作用单向数据流 Link $ 语法双向绑定跨组件同步篇 69父改子同步子改父同步双向——续「ArkUI 状态联动」深水区讲状态联动深水区Watch 副作用回调槽边界 Link $ 语法双向绑定边界。系列预告下篇篇 70讲 Provide/Consume 跨层级传递边界祖辈 Provide 后辈 Consume 跨层级传递根因续「ArkUI 状态联动」深水区。五阶段哲学体系类型哲学50-52→ 作用域哲学53-55→ 状态哲学56-59→ 渎染哲学60-62→ 组件设计63-67→ 状态联动深水区68讲清 ArkTS/ArkUI 进阶哲学。能力系列回链能力系列篇本文进阶点篇 20 Link 用法Link $ 语法双向绑定边界根因父改子同步子改父同步双向篇 13 State 基础用法状态哲学State 赋值就刷 UI 依赖追踪篇 59 Watch 用法状态联动深水区Watch 副作用回调槽 vs onClick 直接调副作用边界真机 demo 完整代码// 篇 69 demoLink 跨组件双向同步边界——父改子同步 子改父同步双向 // 对比Link 双向同步父改子子改父都同步vs Prop 单向同步父改子同步子改父不同步 // ✅ 子组件Link 双向同步——父改子同步 子改父同步 Component struct ChildLink { Link childCount: number // ✅ Link 双向同步父改 childCount 子同步 子改 childCount 父同步 build() { Column({ space: 8 }) { Text(子组件 ChildLinkLink 双向同步) .fontSize(13).fontColor(#2563eb).fontWeight(FontWeight.Bold) Text(childCount ${this.childCount}) .fontSize(16).fontWeight(FontWeight.Bold) Button(子改 childCountLink 子改父同步) .width(92%).height(40).fontSize(12) .onClick(() { this.childCount // ✅ Link 子改 childCount → 父 State childCount 同步 }) } .width(92%).padding(10).backgroundColor(#e0f0ff).borderRadius(8) } } // ⚠ 对比组件Prop 单向同步——父改子同步 子改父不同步Prop 子改不回传父 Component struct ChildProp { Prop childCount: number // ⚠ Prop 单向同步父改 childCount 子同步 子改 childCount 父不同步 build() { Column({ space: 8 }) { Text(对比组件 ChildPropProp 单向同步) .fontSize(13).fontColor(#ff6600).fontWeight(FontWeight.Bold) Text(childCount ${this.childCount}) .fontSize(16).fontWeight(FontWeight.Bold) Button(子改 childCountProp 子改父不同步) .width(92%).height(40).fontSize(12) .onClick(() { this.childCount // ⚠ Prop 子改 childCount → 父 State childCount 不同步只改子本地副本 }) } .width(92%).padding(10).backgroundColor(#fff0e0).borderRadius(8) } } Entry Component struct Index { State linkCount: number 0 // ✅ Link 父状态父改子同步 子改父同步双向 State propCount: number 0 // ⚠ Prop 父状态父改子同步单向子改父不同步 State log: string (未操作) build() { Column({ space: 12 }) { Text(篇 69 配图Link 跨组件双向同步边界) .fontSize(18).fontWeight(FontWeight.Bold).margin({ top: 20, bottom: 8 }) Text(Link 双向同步父改子子改父都同步vs Prop 单向同步父改子同步子改父不同步) .fontSize(11).fontColor(#888).margin({ bottom: 12 }) // ✅ Link 路径父改 linkCount → 子 Link 同步 子改 childCount → 父 State 同步 Column({ space: 6 }) { Text(父 linkCount ${this.linkCount}).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#2563eb) Button(父改 linkCountLink 父改子同步) .width(92%).height(40).fontSize(12) .onClick(() { this.linkCount // ✅ Link 父改 linkCount → 子 Link childCount 同步 this.log 父改 linkCount${this.linkCount}Link 父改子同步 }) ChildLink({ childCount: $linkCount }) // ✅ Link $ 语法双向绑定 } .width(92%).padding(10).backgroundColor(#f5f5f5).borderRadius(8) // ⚠ Prop 路径父改 propCount → 子 Prop 同步 子改 childCount → 父 State 不同步 Column({ space: 6 }) { Text(父 propCount ${this.propCount}).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#ff6600) Button(父改 propCountProp 父改子同步) .width(92%).height(40).fontSize(12) .onClick(() { this.propCount // ✅ Prop 父改 propCount → 子 Prop childCount 同步 this.log 父改 propCount${this.propCount}Prop 父改子同步 }) ChildProp({ childCount: this.propCount }) // ⚠ Prop 单向同步无 $ 语法 } .width(92%).padding(10).backgroundColor(#f5f5f5).borderRadius(8) Text(日志${this.log}).fontSize(11).fontColor(#333).margin({ top: 4 }) } .width(100%).height(100%).alignItems(HorizontalAlign.Center) } }写鸿蒙 ArkUI 记住Link 不是单向传值是 $ 语法双向绑定的跨组件同步——Link 荬饰器 $ 语法编译期绑父子双向同步槽父改子同步子改父同步双向。根因不是单向传值是 $ 语法双向绑定——Link $ 语法编译期绑父子双向同步槽父 State ↔ 子 Link 双向同步 Prop 传值单向同步父改子同步子改父不同步子改只改本地副本 Link $ 语法必须配 State$ 语法双向绑定要求父 State 响应式 Link $ 语法 vs Prop 传值边界双向同步 vs 单向传值。Link 父子计数器双向同步用 $ 语法双向绑定首选90% 场景Link 父子表单双向同步用 $ 语法双向绑定子改父同步Link 父子对象双向同步用 $ 语法双向绑定对象引用同步。$ 语法双向绑定父子都同步是 ArkUI 状态联动深水区核心