• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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。         |