鸿蒙 状态管理最佳使用方式

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、概述

在HarmonyOS的ArkUI声明式开发范式中,采用MVVM(Model-View-ViewModel)架构模式,UI是应用状态的函数,状态变化驱动UI更新。ArkUI提供了一系列装饰器(如@State、@Prop、@Link、@Provide、@Consume等)来实现数据与视图的绑定。不合理的状态管理会导致UI不一致、不必要的渲染刷新等问题。

二、合理选择装饰器

1. 避免不必要的状态变量

状态变量的管理有额外开销,应仅在必要时使用。普通变量用状态装饰器标记可能导致性能下降。

反例1:冗余的状态变量

@Observed
class UserPosition {
  x: number = 20;
}

@Component
struct UserComponent {
  // 错误:positionObj未关联任何UI组件,不应定义为状态变量
  @State positionObj: UserPosition = new UserPosition(); 
  // 错误:buttonLabel未关联任何UI组件,不应定义为状态变量
  @State buttonLabel: string = '点击我'; 
  
  build() {
    // 这两个状态变量未被使用
  }
}

反例2:只读的状态变量

@Observed
class UserPosition {
  x: number = 20;
}

@Component
struct UserComponent {
  @State positionObj: UserPosition = new UserPosition();
  @State buttonLabel: string = '点击我'; // 只有读取操作
  
  build() {
    Column() {
      Button(this.buttonLabel) // 仅读取值,无修改操作
    }
  }
}

正例:合理使用状态变量

@Observed
class UserPosition {
  x: number = 20;
}

@Component
struct OptimizedComponent {
  @State positionObj: UserPosition = new UserPosition(); // 有读写操作,使用状态变量
  buttonLabel: string = '点击我'; // 仅读取操作,使用普通变量
  
  build() {
    Column() {
      Button(this.buttonLabel)
        .onClick(() => {
          animateTo({ duration: 50 }, () => {
            // 修改状态变量,驱动UI更新
            this.positionObj.x = this.positionObj.x + 50;
          })
        })
    }
    .translate({ x: this.positionObj.x })
  }
}

2. 建议使用临时变量替换状态变量

状态变量变化会触发UI渲染,通过临时变量计算可减少不必要的渲染次数。

反例:直接操作状态变量

@Component
struct LoggerComponent {
  @State logContent: string = '';
  
  // 直接多次修改状态变量
  appendLog(newLog: string) {
    this.logContent += newLog;
    this.logContent += ';';
    this.logContent += '<br/>';
  }
  
  build() {
    Column() {
      Button('添加日志')
        .onClick(() => {
          this.appendLog('用户操作记录');
        })
    }
  }
}

正例:使用临时变量优化

@Entry
@Component
struct OptimizedLogger {
  @State logContent: string = '';
  
  // 使用临时变量,只修改一次状态变量
  appendLog(newLog: string) {
    let tempContent = this.logContent;
    tempContent += newLog;
    tempContent += ';';
    tempContent += '<br/>';
    this.logContent = tempContent; // 单次赋值
  }
  
  build() {
    Column() {
      Button('添加日志')
        .onClick(() => {
          this.appendLog('用户操作记录');
        })
    }
  }
}

三、最小化状态共享范围

1. 组件内独享的状态

使用@State装饰器,状态生命周期与组件同步,修改只触发当前组件重渲染。

@Component
struct ThemeItem {
  @State isSelected: boolean = false; // 组件内独享状态
  
  build() {
    Button(this.isSelected ? '已选' : '未选')
      .onClick(() => {
        this.isSelected = !this.isSelected; // 只更新当前组件
      })
  }
}

2. 组件间共享的状态

按共享范围从小到大选择合适的装饰器:

父子组件共享:使用@Prop和@Link

// 父组件
@Entry
@Component
struct ParentView {
  @State isLoading: boolean = false;
  
  build() {
    Column() {
      ChildComponent({ loading: this.isLoading })
      AnotherChild({ loading: $isLoading })
    }
  }
}

// 子组件
@Component
struct ChildComponent {
  @Prop loading: boolean; // 单向同步
  
  build() {
    Text(this.loading ? '加载中...' : '完成')
  }
}

@Component
struct AnotherChild {
  @Link loading: boolean; // 双向同步
  
  build() {
    Button('切换状态')
      .onClick(() => {
        this.loading = !this.loading;
      })
  }
}

跨组件层级共享:使用@Provide和@Consume或@Provider和@Consumer

四、使用监听和订阅精准控制组件刷新

当多个组件依赖对象中的不同属性时,直接关联该对象会出现改变任一属性所有组件都刷新的现象。可通过以下策略精准控制组件刷新:

1. 属性拆分

将包含多个属性的类拆分为多个单一属性的状态类

反例:所有组件都刷新

@Observed
class UserProfile {
  name: string = '';
  age: number = 0;
  email: string = '';
}

@Component
struct UserCard {
  @ObjectLink profile: UserProfile;
  
  build() {
    // 任何属性变化都会导致整个组件刷新
  }
}

正例:精准控制刷新

@Observed
class UserName {
  value: string = '';
}

@Observed
class UserAge {
  value: number = 0;
}

@Observed
class UserEmail {
  value: string = '';
}

@Component
struct UserNameCard {
  @ObjectLink name: UserName;
  // 只关注name变化
}

@Component
struct UserAgeCard {
  @ObjectLink age: UserAge;
  // 只关注age变化
}

2. 发布订阅模式

当组件关系复杂或跨越层级过多时,推荐使用EventHub或者Emitter自定义事件发布订阅的方式来精确通知特定组件。

五、装饰器选择策略

  1. @State:组件内部私有状态,不需要与父组件或后代组件共享
  2. @Prop:父子组件单向同步,父组件到子组件
  3. @Link:父子组件双向同步
  4. @Provide/@Consume:跨组件层级双向同步(状态管理V1)
  5. @Provider/@Consumer:跨组件层级双向同步(状态管理V2,API 12+)
  6. LocalStorage:页面内跨组件树状态共享
  7. AppStorage:应用全局状态共享

六、建议

  1. 使用Code Linter工具:检测冗余状态变量和未变化状态变量
  2. 复杂计算使用临时变量:减少状态变量直接操作次数
  3. 合理划分状态范围:避免不必要的全局状态,按需共享
  4. 精准控制刷新:对于集合数据,使用细粒度监听策略
  5. 避免在键值中包含索引:在ForEach循环中,避免使用索引作为key的一部分
  6. 使用防抖节流:对于频繁更新的状态,考虑使用防抖或节流机制

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值