1# getTarget接口:获取状态管理框架代理前的原始对象 2 3为了获取状态管理框架代理前的原始对象,开发者可以使用[getTarget接口](../reference/apis-arkui/js-apis-StateManagement.md#gettarget)。 4 5在阅读本文档前,建议提前阅读:[\@Observed](./arkts-observed-and-objectlink.md),[\@ObservedV2](./arkts-new-observedV2-and-trace.md)。 6 7>**说明:** 8> 9>从API version 12开始,开发者可以使用UIUtils中的getTarget接口获取状态管理框架代理前的原始对象。 10 11## 概述 12 13状态管理框架会对Class、Date、Map、Set、Array类型的原始对象添加代理,用于观测属性变化与API调用。这一层代理会使得变量类型改变,在类型判断、NAPI调用等场景,会由于类型并非原始对象的类型产生预料之外的结果。 14 15- 使用getTarget接口需要导入UIUtils工具。 16 17 ```ts 18 import { UIUtils } from '@kit.ArkUI'; 19 ``` 20 21- 状态管理V1中,会给\@Observed装饰的类对象以及使用状态变量装饰器如\@State装饰的Class、Date、Map、Set、Array添加一层代理用于观测一层属性或API调用产生的变化。 22- 状态管理V2中,会给使用状态变量装饰器如\@Trace、\@Local装饰的Date、Map、Set、Array添加一层代理用于观测API调用产生的变化。 23 24使用getTarget接口可以获取这些代理对象的原始对象。 25 26## 限制条件 27 28- getTarget仅支持对象类型传参 29 30 ```ts 31 import { UIUtils } from '@kit.ArkUI'; 32 let res = UIUtils.getTarget(2); // 非对象类型入参,错误用法 33 @Observed 34 class Info { 35 name: string = "Tom"; 36 } 37 let info: Info = new Info(); 38 let rawInfo: Info = UIUtils.getTarget(info); // 正确用法 39 ``` 40 41- 更改getTarget获取的原始对象中的内容不会被观察到变化,也不会触发UI刷新。 42 43 ```ts 44 import { UIUtils } from '@kit.ArkUI'; 45 @Observed 46 class Info { 47 name: string = "Tom"; 48 } 49 @Entry 50 @Component 51 struct Index { 52 @State info: Info = new Info(); 53 54 build() { 55 Column() { 56 Text(`info.name: ${this.info.name}`) 57 Button(`更改代理对象的属性`) 58 .onClick(() => { 59 this.info.name = "Alice"; // Text组件能够刷新 60 }) 61 Button(`更改原始对象的属性`) 62 .onClick(() => { 63 let rawInfo: Info = UIUtils.getTarget(this.info); 64 rawInfo.name = "Bob"; // Text组件不能刷新 65 }) 66 } 67 } 68 } 69 ``` 70 71## 使用场景 72 73### 获取状态管理V1代理前的原始对象 74 75状态管理V1有两种场景会给对象增加代理: 76 77【1】\@Observed装饰的类实例。在创建\@Observed装饰的类实例时,会给该实例添加代理。该过程发生在new对象的过程中,没有经过new操作符创建的对象是不被代理的。 78 79```ts 80@Observed 81class ObservedClass { 82 name: string = "Tom"; 83} 84class NonObservedClass { 85 name: string = "Tom"; 86} 87let observedClass: ObservedClass = new ObservedClass(); // 被代理 88let nonObservedClass: NonObservedClass = new NonObservedClass(); // 不被代理 89``` 90 91【2】状态变量装饰器装饰的复杂类型对象。使用\@State、\@Prop等状态变量装饰器装饰Class、Map、Set、Date、Array时,会添加代理。若该对象已经是代理对象,则不会重复创建代理。 92 93```ts 94@Observed 95class ObservedClass { 96 name: string = "Tom"; 97} 98class NonObservedClass { 99 name: string = "Tom"; 100} 101let observedClass: ObservedClass = new ObservedClass(); // 被代理 102let nonObservedClass: NonObservedClass = new NonObservedClass(); // 不被代理 103@Entry 104@Component 105struct Index { 106 @State observedObject: ObservedClass = observedClass; // 已被代理数据不会重复创建代理 107 @State nonObservedObject: NonObservedClass = nonObservedClass; // 创建代理 108 @State numberList: number[] = [1, 2, 3]; // Array类型创建代理 109 @State sampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // Map类型创建代理 110 @State sampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // Set类型创建代理 111 @State sampleDate: Date = new Date(); // Date类型创建代理 112 113 build() { 114 Column() { 115 Text(`this.observedObject === observedClass: ${this.observedObject === observedClass}`) // true 116 Text(`this.nonObservedObject === nonObservedClass: ${this.nonObservedObject === nonObservedClass}`) // false 117 } 118 } 119} 120``` 121 122使用UIUtils.getTarget接口可以获取代理前的原始对象。 123 124```ts 125import { UIUtils } from '@kit.ArkUI'; 126@Observed 127class ObservedClass { 128 name: string = "Tom"; 129} 130class NonObservedClass { 131 name: string = "Tom"; 132} 133let observedClass: ObservedClass = new ObservedClass(); // 被代理 134let nonObservedClass: NonObservedClass = new NonObservedClass(); // 不被代理 135let globalNumberList: number[] = [1, 2, 3]; // 不被代理 136let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理 137let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理 138let globalSampleDate: Date = new Date(); // 不被代理 139@Entry 140@Component 141struct Index { 142 @State observedObject: ObservedClass = observedClass; // 已被代理数据不会重复创建代理 143 @State nonObservedObject: NonObservedClass = nonObservedClass; // 创建代理 144 @State numberList: number[] = globalNumberList; // Array类型创建代理 145 @State sampleMap: Map<number, string> = globalSampleMap; // Map类型创建代理 146 @State sampleSet: Set<number> = globalSampleSet; // Set类型创建代理 147 @State sampleDate: Date = globalSampleDate; // Date类型创建代理 148 149 build() { 150 Column() { 151 Text(`this.observedObject === observedClass: ${this.observedObject === 152 observedClass}`) // true 153 Text(`UIUtils.getTarget(this.nonObservedObject) === nonObservedClass: ${UIUtils.getTarget(this.nonObservedObject) === 154 nonObservedClass}`) // true 155 Text(`UIUtils.getTarget(this.numberList) === globalNumberList: ${UIUtils.getTarget(this.numberList) === 156 globalNumberList}`) // true 157 Text(`UIUtils.getTarget(this.sampleMap) === globalSampleMap: ${UIUtils.getTarget(this.sampleMap) === 158 globalSampleMap}`) // true 159 Text(`UIUtils.getTarget(this.sampleSet) === globalSampleSet: ${UIUtils.getTarget(this.sampleSet) === 160 globalSampleSet}`) // true 161 Text(`UIUtils.getTarget(this.sampleDate) === globalSampleDate: ${UIUtils.getTarget(this.sampleDate) === 162 globalSampleDate}`) // true 163 } 164 } 165} 166``` 167 168### 获取状态管理V2代理前的原始对象 169 170状态管理V2会给状态变量装饰器如\@Trace、\@Local装饰的Map、Set、Date、Array添加一层代理。和V1不同的是,状态管理V2不会对类对象实例进行代理。 171 172```ts 173@ObservedV2 174class ObservedClass { 175 @Trace name: string = "Tom"; 176} 177let globalObservedObject: ObservedClass = new ObservedClass(); // 不被代理 178let globalNumberList: number[] = [1, 2, 3]; // 不被代理 179let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理 180let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理 181let globalSampleDate: Date = new Date(); // 不被代理 182@Entry 183@ComponentV2 184struct Index { 185 @Local observedObject: ObservedClass = globalObservedObject; // V2中对象不被代理 186 @Local numberList: number[] = globalNumberList; // Array类型创建代理 187 @Local sampleMap: Map<number, string> = globalSampleMap; // Map类型创建代理 188 @Local sampleSet: Set<number> = globalSampleSet; // Set类型创建代理 189 @Local sampleDate: Date = globalSampleDate; // Date类型创建代理 190 191 build() { 192 Column() { 193 Text(`this.observedObject === globalObservedObject ${this.observedObject === globalObservedObject}`) // true 194 Text(`this.numberList === globalNumberList ${this.numberList === globalNumberList}`) // false 195 } 196 } 197} 198``` 199 200使用UIUtils.getTarget接口可以获取代理前的原始对象。 201 202```ts 203import { UIUtils } from '@kit.ArkUI'; 204@ObservedV2 205class ObservedClass { 206 @Trace name: string = "Tom"; 207} 208let globalObservedObject: ObservedClass = new ObservedClass(); // 不被代理 209let globalNumberList: number[] = [1, 2, 3]; // 不被代理 210let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理 211let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理 212let globalSampleDate: Date = new Date(); // 不被代理 213@Entry 214@ComponentV2 215struct Index { 216 @Local observedObject: ObservedClass = globalObservedObject; // V2中对象不被代理 217 @Local numberList: number[] = globalNumberList; // Array类型创建代理 218 @Local sampleMap: Map<number, string> = globalSampleMap; // Map类型创建代理 219 @Local sampleSet: Set<number> = globalSampleSet; // Set类型创建代理 220 @Local sampleDate: Date = globalSampleDate; // Date类型创建代理 221 222 build() { 223 Column() { 224 Text(`this.observedObject === globalObservedObject ${this.observedObject === 225 globalObservedObject}`) // true 226 Text(`UIUtils.getTarget(this.numberList) === globalNumberList: ${UIUtils.getTarget(this.numberList) === 227 globalNumberList}`) // true 228 Text(`UIUtils.getTarget(this.sampleMap) === globalSampleMAP: ${UIUtils.getTarget(this.sampleMap) === 229 globalSampleMap}`) // true 230 Text(`UIUtils.getTarget(this.sampleSet) === globalSampleSet: ${UIUtils.getTarget(this.sampleSet) === 231 globalSampleSet}`) // true 232 Text(`UIUtils.getTarget(this.sampleDate) === globalSampleDate: ${UIUtils.getTarget(this.sampleDate) === 233 globalSampleDate}`) // true 234 } 235 } 236} 237``` 238 239状态管理V2装饰器会为装饰的变量生成getter和setter方法,同时为原有变量名添加"\_\_ob\_"的前缀。出于性能考虑,getTarget接口不会对V2装饰器生成的前缀进行处理,因此向getTarget接口传入\@ObservedV2装饰的类对象实例时,返回的对象依旧为对象本身,且被\@Trace装饰的属性名仍有"\_\_ob\_"前缀。 240 241该前缀会导致某些NAPI接口无法按预期处理对象的属性,以下面的对象为例,目前已知影响的NAPI接口如下: 242 243```ts 244// ObservedV2装饰的类 245@ObservedV2 246class Info { 247 @Trace name: string = "Tom"; 248 @Trace age: number = 24; 249} 250let info: Info = new Info(); // NAPI接口传入info实例 251``` 252 253| 影响接口名 | 影响结果 | 254| ----------------------- | ---------------------------------------------- | 255| napi_get_property_names | 返回值为"\_\_ob\_name","\_\_ob\_age"。 | 256| napi_set_property | 使用"name","\_\_ob\_name"均能赋值成功。 | 257| napi_get_property | 使用"name","\_\_ob\_name"均能获取到值。 | 258| napi_has_property | 使用"name","\_\_ob\_name"均返回true。 | 259| napi_delete_property | 删除属性时需要加上"\_\_ob\_"前缀才能删除成功。 | 260| napi_has_own_property | 使用"name","\_\_ob\_name"均返回true。 | 261| napi_set_named_property | 使用"name","\_\_ob\_name"均能赋值成功。 | 262| napi_get_named_property | 使用"name","\_\_ob\_name"均能获取到值。 | 263| napi_has_named_property | 使用"name","\_\_ob\_name"均返回true。 |