• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 自定义组件冻结功能
2
3当@ComponentV2装饰的自定义组件处于非激活状态时,状态变量将不响应更新,即@Monitor不会调用,状态变量关联的节点不会刷新。通过freezeWhenInactive属性来决定是否使用冻结功能,不传参数时默认不使用。支持的场景有:页面路由,TabContent,Navigation。
4
5
6> **说明:**
7>
8> 从API version 12开始,支持@ComponentV2装饰的自定义组件冻结功能。
9>
10> 和@Component的组件冻结不同, @ComponentV2装饰的自定义组件不支持LazyForEach场景下的缓存节点组件冻结。
11
12
13## 当前支持的场景
14
15### 页面路由
16
17- 当页面A调用router.pushUrl接口跳转到页面B时,页面A为隐藏不可见状态,此时如果更新页面A中的状态变量,不会触发页面A刷新。
18
19
20页面A:
21
22```ts
23import { router } from '@kit.ArkUI';
24
25@ObservedV2
26export class Book {
27  @Trace page: number = 100;
28
29  constructor(page: number) {
30    this.page = page;
31  }
32
33  @Monitor("page")
34  onPageChange(monitor: IMonitor) {
35    console.log(`Page change : ${this.page}`);
36  }
37}
38
39export let book: Book = new Book(100);
40
41@Entry
42@ComponentV2({ freezeWhenInactive: true })
43export struct FirstTest {
44  build() {
45    Column() {
46      Text(`From fist Page ${book.page}`).fontSize(50)
47      Button('first page + 1').fontSize(30)
48        .onClick(() => {
49          book.page += 1;
50        })
51      Button('go to next page').fontSize(30)
52        .onClick(() => {
53          router.pushUrl({ url: 'pages/Page' });
54        })
55    }
56  }
57}
58```
59
60页面B:
61
62```ts
63import { router } from '@kit.ArkUI';
64import { book } from './Index';
65
66@Entry
67@ComponentV2({ freezeWhenInactive: true })
68struct SecondTest {
69  build() {
70    Column() {
71      Text(`second Page ${book.page}`).fontSize(50)
72      Button('Back')
73        .onClick(() => {
74          router.back();
75        })
76      Button('second page + 2').fontSize(30)
77        .onClick(() => {
78          book.page += 2;
79        })
80
81    }
82  }
83}
84```
85
86在上面的示例中:
87
881.点击页面A中的Button “first page + 1”,book变量的page属性改变,@Monitor中注册的方法onPageChange会被调用。
89
902.通过router.pushUrl({url: 'pages/Page'}),跳转到页面B,页面A隐藏,状态由active变为inactive。
91
923.点击页面B中的Button “second page + 2”,Monitor中注册的方法onPageChange会被调用。
93
944.点击“back”,页面B被销毁,页面A的状态由inactive变为active,对应的Text显示内容改变。
95
96
97### TabContent
98
99- 对Tabs中当前不可见的TabContent进行冻结,不会触发组件的更新。
100
101- 需要注意的是:在首次渲染的时候,Tab只会创建当前正在显示的TabContent,当切换全部的TabContent后,TabContent才会被全部创建。
102
103```ts
104@Entry
105@ComponentV2
106struct TabContentTest {
107  @Local message: number = 0;
108  @Local data: number[] = [0, 1];
109
110  build() {
111    Row() {
112      Column() {
113        Button('change message').onClick(() => {
114          this.message++;
115        })
116
117        Tabs() {
118          ForEach(this.data, (item: number) => {
119            TabContent() {
120              FreezeChild({ message: this.message, index: item })
121            }.tabBar(`tab${item}`)
122          }, (item: number) => item.toString())
123        }
124      }
125      .width('100%')
126    }
127    .height('100%')
128  }
129}
130
131@ComponentV2({ freezeWhenInactive: true })
132struct FreezeChild {
133  @Param message: number = 0;
134  @Param index: number = 0;
135
136  @Monitor('message') onMessageUpdated(mon: IMonitor) {
137    console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`);
138  }
139
140  build() {
141    Text("message" + `${this.message}, index: ${this.index}`)
142      .fontSize(50)
143      .fontWeight(FontWeight.Bold)
144  }
145}
146```
147
148在上面的示例中:
149
1501.点击“change message”更改message的值,当前正在显示的TabContent组件中的@Monitor中注册的方法onMessageUpdated被触发。
151
1522.点击TabBar“tab1”切换到另外的TabContent,TabContent状态由inactive变为active,对应的@Monitor中注册的方法onMessageUpdated被触发。
153
1543.再次点击“change message”更改message的值,仅当前显示的TabContent子组件中的@Monitor中注册的方法onMessageUpdated被触发。
155
156
157### Navigation
158
159- 对当前不可见的页面进行冻结,不会触发组件的更新,当返回该页面时,触发@Monitor回调进行刷新。
160
161```ts
162@Entry
163@ComponentV2
164struct MyNavigationTestStack {
165  @Provider('pageInfo') pageInfo: NavPathStack = new NavPathStack();
166  @Local message: number = 0;
167
168  @Monitor('message') info() {
169    console.info(`freeze-test MyNavigation message callback ${this.message}`);
170  }
171
172  @Builder
173  PageMap(name: string) {
174    if (name === 'pageOne') {
175      pageOneStack({ message: this.message })
176    } else if (name === 'pageTwo') {
177      pageTwoStack({ message: this.message })
178    } else if (name === 'pageThree') {
179      pageThreeStack({ message: this.message })
180    }
181  }
182
183  build() {
184    Column() {
185      Button('change message')
186        .onClick(() => {
187          this.message++;
188        })
189      Navigation(this.pageInfo) {
190        Column() {
191          Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
192            .onClick(() => {
193              this.pageInfo.pushPath({ name: 'pageOne' }); //将name指定的NavDestination页面信息入栈
194            })
195        }
196      }.title('NavIndex')
197      .navDestination(this.PageMap)
198      .mode(NavigationMode.Stack)
199    }
200  }
201}
202
203@ComponentV2
204struct pageOneStack {
205  @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack();
206  @Local index: number = 1;
207  @Param message: number = 0;
208
209  build() {
210    NavDestination() {
211      Column() {
212        NavigationContentMsgStack({ message: this.message, index: this.index })
213        Text("cur stack size:" + `${this.pageInfo.size()}`)
214          .fontSize(30)
215        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
216          .onClick(() => {
217            this.pageInfo.pushPathByName('pageTwo', null);
218          })
219        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule })
220          .onClick(() => {
221            this.pageInfo.pop();
222          })
223      }.width('100%').height('100%')
224    }.title('pageOne')
225    .onBackPressed(() => {
226      this.pageInfo.pop();
227      return true;
228    })
229  }
230}
231
232@ComponentV2
233struct pageTwoStack {
234  @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack();
235  @Local index: number = 2;
236  @Param message: number = 0;
237
238  build() {
239    NavDestination() {
240      Column() {
241        NavigationContentMsgStack({ message: this.message, index: this.index })
242        Text("cur stack size:" + `${this.pageInfo.size()}`)
243          .fontSize(30)
244        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
245          .onClick(() => {
246            this.pageInfo.pushPathByName('pageThree', null);
247          })
248        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule })
249          .onClick(() => {
250            this.pageInfo.pop();
251          })
252      }
253    }.title('pageTwo')
254    .onBackPressed(() => {
255      this.pageInfo.pop();
256      return true;
257    })
258  }
259}
260
261@ComponentV2
262struct pageThreeStack {
263  @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack();
264  @Local index: number = 3;
265  @Param message: number = 0;
266
267  build() {
268    NavDestination() {
269      Column() {
270        NavigationContentMsgStack({ message: this.message, index: this.index })
271        Text("cur stack size:" + `${this.pageInfo.size()}`)
272          .fontSize(30)
273        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
274          .height(40)
275          .onClick(() => {
276            this.pageInfo.pushPathByName('pageOne', null);
277          })
278        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule })
279          .height(40)
280          .onClick(() => {
281            this.pageInfo.pop();
282          })
283      }
284    }.title('pageThree')
285    .onBackPressed(() => {
286      this.pageInfo.pop();
287      return true;
288    })
289  }
290}
291
292@ComponentV2({ freezeWhenInactive: true })
293struct NavigationContentMsgStack {
294  @Param message: number = 0;
295  @Param index: number = 0;
296
297  @Monitor('message') info() {
298    console.info(`freeze-test NavigationContent message callback ${this.message}`);
299    console.info(`freeze-test ---- called by content ${this.index}`);
300  }
301
302  build() {
303    Column() {
304      Text("msg:" + `${this.message}`)
305        .fontSize(30)
306    }
307  }
308}
309```
310
311在上面的示例中:
312
3131.点击“change message”更改message的值,当前正在显示的MyNavigationTestStack组件中的@Monitor中注册的方法info被触发。
314
3152.点击“Next Page”切换到PageOne,创建pageOneStack节点。
316
3173.再次点击“change message”更改message的值,仅pageOneStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。
318
3194.再次点击“Next Page”切换到PageTwo,创建pageTwoStack节点。
320
3215.再次点击“change message”更改message的值,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。
322
3236.再次点击“Next Page”切换到PageThree,创建pageThreeStack节点。
324
3257.再次点击“change message”更改message的值,仅pageThreeStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。
326
3278.点击“Back Page”回到PageTwo,此时,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。
328
3299.再次点击“Back Page”回到PageOne,此时,仅pageOneStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。
330
33110.再次点击“Back Page”回到初始页。