HarmonyOS7 状态管理全家桶:@State、@Prop、@Link 到底怎么选? 文章目录前言为什么状态管理这么头疼三种装饰器对比场景一父子单向传递Prop场景二父子双向同步Link场景三跨层级传递Provide Consume场景四列表中的状态管理选型决策树写在最后前言刚学 ArkUI 的时候状态管理那几个装饰器把我搞懵了——State、Prop、Link长得像亲兄弟用起来完全不一样。照着官方文档抄了一遍代码数据就是不同步debug 了两个小时才发现用错了装饰器。选错装饰器轻则数据不同步重则整个组件不刷新。今天把这三个装饰器掰碎了讲看完你就知道每个场景该用谁。为什么状态管理这么头疼ArkUI 是声明式框架UI 是状态的函数状态变了UI 自动更新。但问题来了——父组件的数据子组件能不能改改了之后父组件知不知道孙组件要访问祖先的数据怎么办这些问题的答案就藏在装饰器的选择里。选对了数据流通顺选错了刷新丢失、数据不同步满屏 bug。三种装饰器对比先把最核心的区别摆出来维度StatePropLink数据流向组件内部自给自足父 → 子单向父 ↔ 子双向能否修改随便改能改但不通知父组件能改同步通知父组件类型限制支持所有类型只支持简单类型string/number/boolean只支持简单类型父组件传参不需要子组件({ prop: this.xxx })子组件({ link: $xxx })|性能影响| 低 | 低 |稍高双向绑定有监听开销 ||使用场景| 组件私有状态 | 展示型子组件 | 表单、开关等需回传的子组件 |划重点Prop 改了不通知爹Link 改了会通知爹。就这一句话的区别。场景一父子单向传递Prop父组件有个用户名子组件只负责展示不需要回传。Componentstruct ParentA{Stateusername:string鸿蒙开发者build(){Column(){Text(父组件this.username).fontSize(20)ChildA({name:this.username})}}}Componentstruct ChildA{Propname:stringbuild(){Text(子组件展示this.name).fontSize(16).fontColor(#666)}}关键代码讲解Prop name: string—— 子组件用 Prop 接收类型必须和父组件传的一致ChildA({ name: this.username })—— 父组件通过构造参数传递注意不是$语法父组件修改username子组件会跟着更新但子组件改name父组件的username不变这是只读展示场景的标准用法。子组件像是拿了一份复印件随便画原件不受影响。场景二父子双向同步Link子组件是个开关用户拨动开关后父组件的状态也要同步更新。Componentstruct ParentB{StateisDarkMode:booleanfalsebuild(){Column(){Text(当前模式(this.isDarkMode?深色:浅色)).fontSize(20)ToggleChild({mode:$isDarkMode})}}}Componentstruct ToggleChild{Linkmode:booleanbuild(){Toggle({type:ToggleType.Switch,isOn:this.mode}).onChange((value:boolean){this.modevalue})}}关键代码讲解Link mode: boolean—— 子组件用 Link 声明建立双向绑定ToggleChild({ mode: $isDarkMode })——注意$符号这是 Link 传参的固定语法this.mode value—— 子组件修改 Link 变量父组件的isDarkMode同步更新$语法就是告诉框架这不是传值是传引用两边绑定了。场景三跨层级传递Provide Consume爷组件要给孙组件传数据中间隔了一层用 Prop/Link 得一层层透传太恶心了。HarmonyOS7 提供了Provide和Consume解决这个问题。Componentstruct Grandparent{Providetheme:stringbluebuild(){Column(){Text(爷爷的主题色this.theme)MiddleLayer()}}}Componentstruct MiddleLayer{build(){Column(){Text(中间层我不关心主题)Grandchild()}}}Componentstruct Grandchild{Consumetheme:stringbuild(){Text(孙子收到的主题色this.theme).fontColor(this.themeblue?#1890ff:#52c41a)}}关键代码讲解Provide theme—— 祖先组件提供数据自动向下广播Consume theme—— 后代组件消费数据按变量名匹配不需要中间层转发中间层MiddleLayer完全不需要知道 theme 的存在这招在大型项目中特别好用。全局主题、用户信息这类到处都要用的数据用 Provide/Consume 最省心。场景四列表中的状态管理列表里每个 item 都有独立状态比如选中态这是最容易出 bug 的地方。Componentstruct ListDemo{Stateitems:SelectItem[][{id:1,name:ArkTS,selected:false},{id:2,name:ArkUI,selected:false},{id:3,name:Flex,selected:true}]build(){List(){ForEach(this.items,(item:SelectItem){ListItem(){ItemRow({name:item.name,selected:item.selected,onToggle:(){item.selected!item.selected}})}},(item:SelectItem)item.id.toString())}}}Componentstruct ItemRow{Propname:stringPropselected:booleanonToggle:()void(){}build(){Row(){Text(this.name)Checkbox().select(this.selected).onChange(()this.onToggle())}}}关键代码讲解列表项状态通过Prop传入单项选中状态用 Prop 就够了回调函数onToggle在父组件中修改State items触发列表刷新ForEach 的第三个参数(item) item.id.toString()是键值生成器别漏了否则列表更新会出问题选型决策树一张图帮你快速决策需要状态管理 │ ├─ 组件自己用不需要传给子组件 │ └─ ✅ State │ ├─ 传给子组件子组件只展示不改 │ └─ ✅ Prop │ ├─ 传给子组件子组件改了要同步回来 │ └─ ✅ Link │ ├─ 跨多层组件传递 │ └─ ✅ Provide Consume │ └─ 列表中的单项状态 └─ ✅ State父 Prop子 回调写在最后状态管理就一句话数据谁拥有修改权就归谁。子组件要改父组件的数据要么用 Link 双向绑定要么用回调函数通知父组件自己改。别偷懒直接改 Prop改了也白改。记住这个原则状态管理的 90% 的坑都能避开。