鸿蒙状态管理之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;
  }
}
相关推荐
后台模板学习24 分钟前
从0到1用Vite构建电商订单系统前端性能优化方案
前端
Wang's Blog24 分钟前
Vibe Coding一人即团队系列58: HTML演示文稿自动生成
前端·人工智能·html
FoldWinCard40 分钟前
基于k8s高可用web集群
前端·容器·kubernetes
神探小白牙41 分钟前
海康视频在vue2.0中的使用
前端·音视频
大雷神41 分钟前
HarmonyOS ArkGraphics 2D可变帧率实操——同屏对比30、60和90 FPS动画
华为·harmonyos
OH_TPC1 小时前
【鸿蒙优选三方库】@react-native-ohos/react-native-fast-image:高性能图片加载与缓存组件
缓存·华为·harmonyos·鸿蒙
Tanjia_kiki1 小时前
谷歌浏览器中F12编辑并重发请求
开发语言·前端·javascript
honkun61 小时前
vue 表格组件 vxe-table 实现拖拽列字段自动生成透视表汇总
前端·javascript·vue.js
新中地GIS开发老师1 小时前
WebGIS开发入门 | 从 Web 开发到地图开发,差别在哪儿?
前端·vue·webgis
IMPYLH1 小时前
HTML 的 <p> 元素
前端·javascript·html