鸿蒙状态管理之State

在组件内使用,@State使变量拥有状态属性,在更改的时候,能够触发UI刷新。

使用限制

  1. @State装饰的变量必须初始化,否则编译期会报错。
  2. 不支持装饰Function类型变量。否则会抛出运行时的错误
  3. 当变量的类型为集合(Array,Map,Set)时,对集合的增删改,能够触发UI刷新,但对集合里的对象的属性进行修改时,则观察不到
  4. 当变量的类型为对象时,修改对象的嵌套属性,无法触发UI刷新
  5. 不允许在build里改变状态变量

行为

  1. 当状态变量被改变时,查询依赖该状态变量的组件;
  2. 执行依赖该状态变量的组件的更新方法,组件更新渲染;
  3. 和该状态变量不相关的组件或者UI描述不会发生重新渲染,从而实现页面渲染的按需更新。

原理

被@State修饰的变量(如@State message:string='hello'),编译之后会变成ObservedPropertySimplePU(普通类型属性)或者ObservedPropertyObjectPU(对象类型属性)这个类型的对象,都继承自ObservedPropertyPU,这个对象会持有三个属性,原始值,组件实例以及属性名(__message:ObservedPropertySimplePU= ObservedPropertySimplePU("hello", obj,"message")),当修改message的时候,会调用__message的set方法,在set方法内部,会修改原始值,然后经过一系列的调用,最终会调用组件实例的viewPropertyHasChanged方法,在这个方法里,会执行this.markNeedUpdate(),把这个组件标记为需要更新

当State修饰的变量类型是对象,编译之后的对象持有的原始值,会用Proxy再包装一层,这样在修改对象属性的时候,也能够监测到,通知组件更新,State不支持嵌套属性的原因是只用Proxy包装了一层

伪代码实现

kotlin 复制代码
// 源代码
class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
}
@Component
export struct MyComponent {
  @State message: string = "hello"
  @State person: Person = new Person('arkts')

  build() {
    Column(){
      Text(this.message).fontColor(Color.Red)
      Text(this.person.name).fontColor(Color.Red)
      Button('点我').onClick(()=>{
        this.message = 'Hello ArkTs'
      })
      Button('修改名字').onClick(()=>{
        this.person.name = '鸿蒙'
      })
    }
  }
}
//伪代码

class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
}

class MyComponent extends ViewPu {
  message = new ObservedPropertySimplePU("hello", this, "message");
  person = new ObservedPropertyObjectPU(new Person('ArkTs'), this, 'person')

  build() {
    Column(){
      Text(this.message).fontColor(Color.Red)
      Text(this.person.name).fontColor(Color.Red)
      Button('点我').onClick(()=>{
        this.message.set('Hello ArkTs')
      })
      Button('修改名字').onClick(()=>{
        this.person.name = '鸿蒙'
      })
    }
  }
}
class ObservedPropertyObjectPU<T> extends ObservedPropertyPU<T> {}
class ObservedPropertySimplePU<T> extends ObservedPropertyPU<T> {}
// ObservedObject继承自ExtendableProxy,会返回一个Proxy对象
class ObservedPropertyPU<T> {
  wrappedValue_: T 
  owningView: ViewPu
  propertyName: string

  set(newValue: T){
    if (this.wrappedValue_ === newValue) {
      return;
    }
    const oldValue = this.wrappedValue_;
    if (this.setValueInternal(newValue)) {
     this.notifyPropertyHasChangedPU()
    }
  }

  get(){
    return this.wrappedValue_
  }

   private setValueInternal(newValue: T): boolean {
    if (newValue === this.wrappedValue_) {
      return false;
    }
    if (!newValue || typeof newValue !== 'object') {
      this.wrappedValue_ = newValue;
    } else if (ObservedObject.IsObservedObject(newValue)) {
      this.wrappedValue_ = newValue;
      // 把wrappedValue_和this关联到一起,方便wrappedValue_监测自己属性变化的时候,通知this
      ObservedObject.addOwningProperty(newValue, this);
    } else {
      this.wrappedValue_ = ObservedObject.createNew(newValue, this);
      // 把wrappedValue_和this关联到一起,方便wrappedValue_监测自己属性变化的时候,通知this
      ObservedObject.addOwningProperty(this.wrappedValue_, this);
    }
    return true;
  }

    protected notifyPropertyHasChangedPU() : void {
    if (this.owningView_) {
      this.owningView_.markNeedUpdate()
    //  this.owningView_.viewPropertyHasChanged(this.propertyName); 会在这个方法内部调用this.markNeedUpdate();
    }
  }
}
class ExtendableProxy {
  constructor(obj: Object, handler: SubscribableHandler) {
    return new Proxy(obj, handler);
  }
}
class ObservedObject extends ExtendableProxy {
    public static createNew<T extends Object>(rawObject: T, owningProperty: IPropertySubscriber): T {
    //判断是否已经是  ObservedObject
    if (ObservedObject.IsObservedObject(rawObject)) {
      ObservedObject.addOwningProperty(rawObject, owningProperty);
      return rawObject;
    }

    return ObservedObject.createNewInternal<T>(rawObject, owningProperty);
  }

  public static createNewInternal<T extends Object>(rawObject: T, owningProperty: IPropertySubscriber): T {
    let proxiedObject;
    if (rawObject instanceof Map || rawObject instanceof Set) {
      proxiedObject = new ObservedObject<T>(rawObject, new SubscribableMapSetHandler(owningProperty), owningProperty);
    }
    else if (rawObject instanceof Date) {
      proxiedObject = new ObservedObject<T>(rawObject, new SubscribableDateHandler(owningProperty), owningProperty);
    }
    else if (Array.isArray(rawObject)) {
      proxiedObject = new ObservedObject<T>(rawObject, new SubscribableArrayHandler(owningProperty), owningProperty);
    }
    else {
      proxiedObject = new ObservedObject(rawObject, new SubscribableHandler(owningProperty), owningProperty);
    }
    return proxiedObject as T;
  }
}
相关推荐
bin915338 分钟前
DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)示例3: 行选择
前端·javascript·vue.js·ecmascript·deepseek
MardaWang8 小时前
鸿蒙开发中,数据持久化之Transaction(事务)的概念及应用
华为·harmonyos
@HNUSTer8 小时前
基于 HTML、CSS 和 JavaScript 的五子棋游戏
前端·javascript·css·游戏·html
百锦再8 小时前
Vue核心知识:动态路由实现完整方案
前端·javascript·vue.js·前端框架·vue·路由·动态
aloha_9 小时前
江口村青年爱心基金会管理制度 - 暂定
前端
拉不动的猪9 小时前
刷刷题24
前端·javascript·面试
aloha_9 小时前
关于成立江口村青年爱心基金会的倡议
前端
Smile_Gently9 小时前
v-code-diff 配置
前端·javascript·vue.js
werch9 小时前
兼容移动端ios,安卓,web端底部软键盘弹出,输入框被遮挡问题
android·前端·ios
成功助力英语中国话9 小时前
visual studio 2022中如何添加项目到解决方案中
前端·ide·visual studio