美文网首页鸿蒙开发笔记
鸿蒙开发笔记-10-其他状态管理:@Watch装饰器,$$语法,

鸿蒙开发笔记-10-其他状态管理:@Watch装饰器,$$语法,

作者: 今阳说 | 来源:发表于2025-03-09 17:20 被阅读0次

一、@Watch 装饰器:状态变化的实时监听者

核心功能
  • @Watch用于监听可观察状态变量(如@State/@Prop/@Link)的变化,在变量值发生变动时触发回调函数。其本质是建立观察者模式,实现组件间状态同步
  • @Watch适用于需要即时响应的状态变化场景,如按钮点击、输入框输入等交互操作
关键特性
  • 监听原理:通过严格相等(===)算法判断状态变量是否更新(false时更新)
  • 初始化阶段:首次组件渲染时不会触发回调,仅后续状态变更时激活,避免初始渲染时的误判
  • 异步安全:禁止在回调中使用async/await,确保渲染线程的及时响应
  • 同步执行:在属性变更后立即触发,优先级高于 UI 渲染
  • 链式更新:若回调中修改其他状态变量,会引发二次监听
  • 节流防抖:高频操作场景添加100ms延迟执行
  • 回调函数:接收一个参数 propName,表示触发变化的属性名
  • 条件过滤:在回调入口添加变更有效性判断
@Watch('onDataUpdate')
private onDataUpdate(propName: string) {
  if (this.prevValue !== this.currentValue) {
    // 实际业务逻辑
  }
}
  • 异步操作规范: 避免在回调中使用async/await,推荐将异步操作封装至独立Service层,通过事件总线与状态管理解耦
  • 触发条件:
    1. 值类型变化(如 stringnumber
    2. 数值变化(如 01
    3. 对象引用变化(如 new Object()
典型使用场景
  • 电商购物车实时计价: 价格计算逻辑与UI更新解耦
@Observed //通过@Observed实现嵌套对象深度监听
class CartItem {
  constructor(public id: number, public price: number) {}
}

@Component
struct CheckoutPanel {
  //@Watch与@Link配合实现双向数据流
  @Link @Watch('updateTotal') cartItems: CartItem[]
  @State total: number = 0

  updateTotal() {
    this.total = this.cartItems.reduce((sum, item) => sum + item.price, 0)
    if (this.total > 1000) this.total *= 0.8 // 满减逻辑
  }

  build() {
    Column() {
      ForEach(this.cartItems, item => 
        Text(`商品${item.id}:¥${item.price}`)
      )
      Text(`实付:¥${this.total.toFixed(2)}`)
    }
  }
}
  • 跨组件状态同步: 支持多页面实时响应主题变化
 @Entry
@Component
struct AppRoot {
  @Provide('theme') @Watch('themeChange') currentTheme: Theme = lightTheme

  themeChange() {
    Logger.log(`主题切换至${this.currentTheme.name}`)
  }
}

@Component
struct SettingsPage {
  @Consume('theme') theme: Theme

  build() {
    Toggle({ checked: this.theme.darkMode })
      .onChange(value => this.theme.darkMode = value)
  }
}
  • 防循环陷阱:避免在回调中直接修改被监听的同一状态变量
@Component struct SafeCounter {
    @State count: number = 0;
    @Watch('onChange')
    onChange() {
      setTimeout(() => this.count++, 0); // 使用中间层避免直接修改
    }
}

二、$$语法:动态模板字符串插值

基础语法

  • 在 ArkTS 中,使用双美元符号 $$ 实现字符串模板插值,将变量动态嵌入字符串中
let name = "Alice";
let greeting = $$`Hello, ${name}!`;
// 输出: Hello, Alice!
  • 与 UI 组件结合
// 简单插值
Text(`Hello, ${this.username}!`);
// 表达式支持
Text(`Price: $${(this.price * 0.9).toFixed(2)}`);

// 动态样式绑定
Row() {
  Text(`Status: ${
    this.status === 'success' ? '✅' : '❌'
  }`)
    .color(this.getStatusColor())
}
  • 避免重复计算:将复杂表达式提取为方法
getFormattedPrice() {
    return this.price.toFixed(2);
}

Text(`Price: $${this.getFormattedPrice()}`)

三、@Track 装饰器:对象属性级更新优化

核心功能
  • 针对class对象的属性级更新优化,解决传统状态管理中全量刷新的性能问题。通过标记特定属性,实现增量渲染
  • @Track推荐用于管理包含多个属性的复杂对象,特别是在频繁更新且需要优化渲染性能的场景
运行机制
  • 白名单机制:仅跟踪被@Track标记的属性,避免未跟踪属性的全量刷新
  • 必要标记原则:所有可能在UI中使用的class属性都应添加@Track
  • 属性级监听:基于JavaScript的Proxy实现属性访问拦截
  • 增量渲染:仅更新被标记属性关联的 UI 节点
  • 兼容性:与 @State、@Link 等装饰器无缝集成
  • 避免混合模式:同一class对象中不应同时存在@Track与非@Track属性
  • 性能考量:对于包含大量属性的复杂对象,优先使用结构化数据(如Record)
典型用例
// 跟踪类定义
class Product {
  @Track price: number = 100;
  @Track stock: number = 50;
  description: string = "Default Product";
}

// 组件使用
@Component struct ProductCard {
  @State product: Product = new Product();
  
  build() {
    Column([
      Text(`Price: $${this.product.price}`).fontSize(20),
      Text(`Stock: ${this.product.stock}`).fontSize(20),
      Button('Update Price').onClick(() => this.product.price += 10)
    ])
  }
}

四、装饰器协同模式

@Watch + @Track 组合

class DataModel {
  @Track name: string;
  @Track age: number;
}

@Component struct DataView {
  @State model: DataModel = new DataModel();
  
  @Watch('name') onChangeName() {
    console.log(`Name changed to ${this.model.name}`);
  }
  
  @Watch('age') onChangeAge() {
    console.log(`Age changed to ${this.model.age}`);
  }
}

$$语法与状态管理结合

@Component struct UserProfile {
  @State user: { name: string; avatar: string } = { name: 'Alice', avatar: 'default' };
  
  build() {
    Column([
      Text(`Welcome, ${this.user.name}!`),
      Image(this.user.avatar)
        .width(100)
        .height(100)
    ])
  }
}

五、总结与建议

  • @Watch 适用于需要即时响应的状态变化场景,如按钮点击、输入框输入等交互操作,使用原则:
    • 回调函数保持纯粹(无副作用)
    • 避免深度嵌套监听(超过 3 层需重构)
    • 使用 debounce 处理高频事件(如输入框)
  • @Track 推荐用于管理包含多个属性的复杂对象,特别是在频繁更新且需要优化渲染性能的场景,使用原则:
    • 所有 UI 可见属性必须标记 @Track
    • 非 UI 属性禁止使用 @Track
    • 复杂对象采用扁平化设计(如 @Track 单独字段)
  • “$$”语法优化:
    • 预计算复杂表达式
    • 使用 computed 属性缓存结果
    @State username: string = 'User';
    @Computed get displayName() {
      return this.username.toUpperCase();
    }
    
    Text(`Hello, ${this.displayName}`)
    

六、常见问题

  • 未触发回调:
    • 检查 @Watch 参数是否与属性名完全匹配
    • 确认状态变量是 @State/@Prop/@Link 装饰的
  • 渲染异常:
    • 确保 @Track 属性在 UI 中合法使用
    • 检查非 @Track 属性是否意外绑定到 UI

我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 “今阳说” 接收我的最新文章

相关文章

  • React开发之【Mobx状态管理,非装饰器模式】

    React开发之【Mobx状态管理,非装饰器模式】 React项目装饰器开启失败, 下面是不用装饰器模式的写法 A...

  • python装饰器

    一、装饰器的理解: 本质:函数(基本语法都是用def关键字定义的);功能:为其他函数添加附加功能(装饰其他函数);...

  • [Python]装饰器使用

    兼容参数版: 非语法糖装饰器 基本语法糖装饰

  • python3基础---详解装饰器

    1、装饰器原理 2、装饰器语法 3、装饰器执行的时间 装饰器在Python解释器执行的时候,就会进行自动装饰,并不...

  • 装饰器

    装饰器属于python高级语法,吉多创始的装饰器也是很精妙,通常我们会把装饰器称作语法糖。就好比,一颗树原本只有树...

  • 透析Python装饰器--透过现象看本质

    》眼花缭乱 Python的装饰器(也称语法糖)大致分为这几类: 无参数装饰器 有参数装饰器 装饰类的装饰器 无参数...

  • 透析Python装饰器-------------透过现象看本质

    》眼花缭乱 Python的装饰器(也称语法糖)大致分为这几类: 无参数装饰器 有参数装饰器 装饰类的装饰器 无参数...

  • 031 Python语法之装饰器

    装饰器 装饰器格式 装饰器 本质是函数 功能是用于装饰其他函数,为其他函数附加其它功能 装饰器的原则 不能修改被装...

  • Python-迭代器

    一、装饰器 开放封闭原则 装饰器的简单实现 装饰器语法糖 装饰有参有返的函数 有参装饰器 wraps修改函数文档注...

  • 13. Python之装饰器

    1 什么是装饰器 2 为何要用装饰器 3 如何使用装饰器 4 语法糖 5 叠加多个装饰器,加载顺序与运行顺序 6 ...

网友评论

    本文标题:鸿蒙开发笔记-10-其他状态管理:@Watch装饰器,$$语法,

    本文链接:https://www.haomeiwen.com/subject/joedmjtx.html