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