当前位置: 首页 > news >正文

HarmonyOS ArkUI 深度响应:@Observed 与 @ObjectLink 完全指南

文章目录

    • 前言
    • 一、问题:对象属性修改不触发更新
      • 1.1 复现"响应式失效"
      • 1.2 简单解决方案:重新赋值
    • 二、@Observed:让类的属性变化可被观察
      • 2.1 @Observed 的作用
      • 2.2 项目中的 @Observed 使用
    • 三、@ObjectLink:在子组件中接收 @Observed 对象
      • 3.1 @State + @Observed 还不够
      • 3.2 @ObjectLink:子组件与父组件共享同一个 @Observed 实例
    • 四、嵌套 @Observed:多层对象结构
      • 4.1 嵌套场景
    • 五、@State vs @Prop vs @ObjectLink 选择指南
    • 总结

前言

在第26篇中我们学到,@State对数组的push操作不会触发 UI 更新,因为@State只观察变量的引用变化。那如果我们有一个复杂对象,修改它的某个属性,UI 要怎么响应?这就是@Observed@ObjectLink要解决的问题。

本项目中MapUtil类使用了@Observed装饰器——本篇将深入解析这对组合的工作原理与实战用法。

一、问题:对象属性修改不触发更新

1.1 复现"响应式失效"

classGasStation{name:string;isOpen:boolean;constructor(name:string){this.name=name;this.isOpen=true;}}@Entry@Componentstruct NotWorkingDemo{@Statestation:GasStation=newGasStation('望京石化');build(){Column({space:16}){Text(this.station.name).fontSize(16)Text(this.station.isOpen?'营业中':'休息中').fontColor(this.station.isOpen?'#52C41A':'#FF4D4F')Button('切换营业状态').onClick(()=>{this.station.isOpen=!this.station.isOpen;// ❌ UI 不会更新!// 因为 this.station 的引用地址没变,// @State 感知不到对象内部属性的变化})}.padding(24).width('100%').justifyContent(FlexAlign.Center)}}

1.2 简单解决方案:重新赋值

// 方案1:重新创建对象(引用改变,触发更新)this.station=newGasStation(this.station.name);// 方案2:展开运算符(对普通对象有效,但 class 实例方法会丢失)// this.station = { ...this.station, isOpen: !this.station.isOpen } as GasStation;// 方案3(推荐):使用 @Observed(下文详解)

二、@Observed:让类的属性变化可被观察

2.1 @Observed 的作用

给类加上@Observed装饰器后,框架会为该类的实例添加Proxy 代理,监听所有属性的get/set操作,从而实现属性级别的精准更新通知。

@Observed// ← 加上这个装饰器classGasStation{name:string;isOpen:boolean;distance:number;constructor(name:string){this.name=name;this.isOpen=true;this.distance=0;}}

2.2 项目中的 @Observed 使用

// MapUtil.ets@Observed// ← 本项目中 MapUtil 类使用了 @ObservedexportclassMapUtil{publicasyncconvertToGCJ02(latitude:number,longitude:number):Promise<mapCommon.LatLng>{// ...}// ...}

提示:MapUtil添加@Observed是因为它被导出为单例mapUtil,通过@ObjectLink在组件间传递时,框架需要感知其属性变化来触发依赖它的 UI 更新。

三、@ObjectLink:在子组件中接收 @Observed 对象

3.1 @State + @Observed 还不够

@ObservedclassStation{name:string='';isOpen:boolean=true;}@Entry@Componentstruct ParentWithObserved{@Statestation:Station=newStation();build(){Column(){// 在父组件直接使用:能响应(@State + @Observed 的属性变化)Text(this.station.isOpen?'营业中':'休息中')// ❌ 问题出现了:把 station 传给子组件后,子组件用 @Prop 接收// 子组件用 @Prop 接收时,@Prop 是深拷贝,修改不会同步到父组件// ChildComp({ station: this.station }) ← @Prop 不能实现双向同步}}}

3.2 @ObjectLink:子组件与父组件共享同一个 @Observed 实例

@ObservedclassStation{id:string;name:string;isOpen:boolean;price:number;constructor(id:string,name:string,price:number){this.id=id;this.name=name;this.isOpen=true;this.price=price;}toggleOpen():void{this.isOpen=!this.isOpen;// 直接修改,自动触发 UI 更新}updatePrice(price:number):void{this.price=price;}}// 子组件:用 @ObjectLink 接收(共享引用,修改即同步)@Componentstruct StationCard{@ObjectLinkstation:Station;// ← @ObjectLink,不是 @Propbuild(){Column({space:8}){Text(this.station.name).fontSize(16).fontWeight(FontWeight.Bold)Row({space:8}){Text(this.station.isOpen?'营业中':'休息中').fontSize(13).fontColor(this.station.isOpen?'#52C41A':'#FF4D4F').padding({left:8,right:8,top:4,bottom:4}).backgroundColor(this.station.isOpen?'#E8F5E9':'#FFF2F0').borderRadius(10)Text(`¥${this.station.price.toFixed(2)}/升`).fontSize(13).fontColor('#1A6FF5')}Row({space:12}){Button(this.station.isOpen?'关闭营业':'开启营业').onClick(()=>{this.station.toggleOpen();// 修改属性,父组件 UI 同步更新!}).fontSize(13).height(36).backgroundColor(this.station.isOpen?'#FF4D4F':'#52C41A').fontColor('#FFFFFF').borderRadius(18)Button('涨价 0.1').onClick(()=>{this.station.updatePrice(this.station.price+0.1);}).fontSize(13).height(36).backgroundColor('#FA8C16').fontColor('#FFFFFF').borderRadius(18)}}.padding(16).width('100%').backgroundColor('#FFFFFF').borderRadius(12).shadow({radius:4,color:'#10000000',offsetX:0,offsetY:2})}}// 父组件@Entry@Componentstruct StationListPage{@Statestations:Station[]=[newStation('001','望京石化',7.85),newStation('002','朝阳石油',7.76),newStation('003','国贸壳牌',8.12),];build(){Column({space:0}){// 统计信息(响应子组件的修改)Row({space:16}){Text(`营业中:${this.stations.filter(s=>s.isOpen).length}`).fontSize(14).fontColor('#52C41A')Text(`关闭中:${this.stations.filter(s=>!s.isOpen).length}`).fontSize(14).fontColor('#FF4D4F')Text(`均价:¥${(this.stations.reduce((s,e)=>s+e.price,0)/this.stations.length).toFixed(2)}`).fontSize(14).fontColor('#1A6FF5')}.padding({left:16,right:16,top:16,bottom:16}).width('100%').backgroundColor('#F0F4FF')List({space:12}){ForEach(this.stations,(station:Station)=>{ListItem(){// 把 Station 实例传给子组件(通过 @ObjectLink 共享)StationCard({station:station})}},(s:Station)=>s.id)}.padding(16).layoutWeight(1)}.width('100%').height('100%').backgroundColor('#F5F7FA')}}

四、嵌套 @Observed:多层对象结构

4.1 嵌套场景

@ObservedclassAddress{city:string;street:string;constructor(city:string,street:string){this.city=city;this.street=street;}}@ObservedclassStationFull{name:string;address:Address;// 嵌套 @Observed 对象price:number;constructor(name:string,city:string,street:string,price:number){this.name=name;this.address=newAddress(city,street);// 也必须是 @Observed 实例this.price=price;}}@Componentstruct NestedDemo{@ObjectLinkstation:StationFull;build(){Column({space:8}){Text(this.station.name)// 嵌套 @Observed 的属性修改也能触发更新Text(`${this.station.address.city}·${this.station.address.street}`).fontColor('#666666')Button('修改城市').onClick(()=>{// 直接修改嵌套对象的属性(需要嵌套对象也是 @Observed)this.station.address.city='上海';})}}}

五、@State vs @Prop vs @ObjectLink 选择指南

父组件有数据 A(@State A) │ ├─ 子组件只读展示 → @Prop(深拷贝,子组件修改不影响父) │ ├─ 子组件需要修改 → @Link(引用同步,双向绑定) │ └─ 数据是 @Observed 类实例,需要精准属性响应 → @ObjectLink
装饰器数据方向数据类型子组件修改影响父?
@Prop父→子任意
@Link双向@State 变量
@ObjectLink双向(属性级)@Observed类实例✅(属性级响应)

总结

@Observed让类实例的属性修改可被 ArkUI 框架感知,@ObjectLink让子组件与父组件共享同一个@Observed实例,从而实现属性级的响应式更新。两者搭配是处理复杂对象(尤其是列表中每一项的状态)的最佳方案。本项目中MapUtil使用@Observed的设计,体现了工程化思维——为未来可能的属性响应式需求预留扩展能力。

http://www.jsqmd.com/news/984035/

相关文章:

  • MailCore IMAP实战教程:10个技巧高效管理邮件文件夹和消息
  • Win32 - 进程间通信(IPC)信号量
  • 终极Outfit字体完整指南:9种字重的免费几何无衬线字体解决方案
  • 5秒极速转换B站缓存视频:m4s-converter完整实战指南
  • 构建跨平台图形应用的双引擎架构:PixiEditor的渲染技术栈深度解析
  • 3.8 使用RPM与YUM
  • 如何免费分析国际象棋对局:Freechess完整使用教程
  • 2026杭州市富阳区家里卫生间漏水、阳台漏水、楼顶漏水、阳台漏水、地下室渗水、阳光房漏水各种房屋漏水情况不用愁!本地防水补漏公司为您排忧解难!精准推荐附近专业防水团队 - 防水百科
  • JimuReport扩展架构深度解析:从权限控制到自定义组件的企业级实战指南
  • KeymouseGo终极指南:三步掌握免费开源鼠标键盘自动化工具
  • Windows上直接运行安卓应用?APK安装器如何革新你的跨平台体验
  • 互联网大厂 Java 求职面试:从基础到复杂的技术考核
  • HarmonyOS ArkUI 弹窗系统:Toast、Dialog、ActionSheet 完全指南
  • 遗传算法进阶核心:选择压力、适应度缩放与精英策略实战解析
  • 2026北京市昌平区家里卫生间漏水、阳台漏水、楼顶漏水、阳台漏水、地下室渗水、阳光房漏水各种房屋漏水情况不用愁!本地防水补漏公司为您排忧解难!精准推荐附近专业防水团队 - 防水百科
  • LMDrive数据集构建完全指南:从零开始创建自动驾驶训练数据
  • 3分钟解锁Cursor Pro完整功能:告别试用限制的终极免费方案
  • 支付宝商家转账,提额成功
  • 如何用Point-E在5分钟内从文本生成3D点云?完整实战指南
  • 2023最新gmplot入门教程:从安装到绘制第一个交互式地图
  • 影刀RPA新手教程_Excel表格读写操作完全指南
  • 3DS宝可梦ROM编辑器:打造专属宝可梦世界的终极工具
  • GIS局部放电在线监测:让电网隐患“无处遁形”
  • 华硕笔记本终极性能调校指南:G-Helper完整教程
  • 题解:学而思编程 斐波那契字符串
  • 3分钟快速汉化Figma界面:设计师必备的终极中文插件指南
  • 2026北京市大兴区家里卫生间漏水、阳台漏水、楼顶漏水、阳台漏水、地下室渗水、阳光房漏水各种房屋漏水情况不用愁!本地防水补漏公司为您排忧解难!精准推荐附近专业防水团队 - 防水百科
  • Zerolang社区贡献指南:如何参与这个革命性编程语言的开发
  • 凡华似金创始人权明受邀出席2026第7届六六盛典暨全国头部装企生态赋能大会分享:以信任密度方程式,为全国装企老板破解增长困局 - GrowthUME
  • 京东商品监控下单神器:告别手速慢的烦恼,让jd-happy帮你自动抢购