在组件内使用,@State使变量拥有状态属性,在更改的时候,能够触发UI刷新。
使用限制
- @State装饰的变量必须初始化,否则编译期会报错。
- 不支持装饰Function类型变量。否则会抛出运行时的错误
- 当变量的类型为集合(Array,Map,Set)时,对集合的增删改,能够触发UI刷新,但对集合里的对象的属性进行修改时,则观察不到
- 当变量的类型为对象时,修改对象的嵌套属性,无法触发UI刷新
- 不允许在build里改变状态变量
行为
- 当状态变量被改变时,查询依赖该状态变量的组件;
- 执行依赖该状态变量的组件的更新方法,组件更新渲染;
- 和该状态变量不相关的组件或者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;
}
}