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”回到初始页。