# 自定义组件冻结功能 当@ComponentV2装饰的自定义组件处于非激活状态时,状态变量将不响应更新,即@Monitor不会调用,状态变量关联的节点不会刷新。通过freezeWhenInactive属性来决定是否使用冻结功能,不传参数时默认不使用。支持的场景有:页面路由,TabContent,Navigation。 > **说明:** > > 从API version 12开始,支持@ComponentV2装饰的自定义组件冻结功能。 > > 和@Component的组件冻结不同, @ComponentV2装饰的自定义组件不支持LazyForEach场景下的缓存节点组件冻结。 ## 当前支持的场景 ### 页面路由 - 当页面A调用router.pushUrl接口跳转到页面B时,页面A为隐藏不可见状态,此时如果更新页面A中的状态变量,不会触发页面A刷新。 页面A: ```ts import { router } from '@kit.ArkUI'; @ObservedV2 export class Book { @Trace page: number = 100; constructor(page: number) { this.page = page; } @Monitor("page") onPageChange(monitor: IMonitor) { console.log(`Page change : ${this.page}`); } } export let book: Book = new Book(100); @Entry @ComponentV2({ freezeWhenInactive: true }) export struct FirstTest { build() { Column() { Text(`From fist Page ${book.page}`).fontSize(50) Button('first page + 1').fontSize(30) .onClick(() => { book.page += 1; }) Button('go to next page').fontSize(30) .onClick(() => { router.pushUrl({ url: 'pages/Page' }); }) } } } ``` 页面B: ```ts import { router } from '@kit.ArkUI'; import { book } from './Index'; @Entry @ComponentV2({ freezeWhenInactive: true }) struct SecondTest { build() { Column() { Text(`second Page ${book.page}`).fontSize(50) Button('Back') .onClick(() => { router.back(); }) Button('second page + 2').fontSize(30) .onClick(() => { book.page += 2; }) } } } ``` 在上面的示例中: 1.点击页面A中的Button “first page + 1”,book变量的page属性改变,@Monitor中注册的方法onPageChange会被调用。 2.通过router.pushUrl({url: 'pages/Page'}),跳转到页面B,页面A隐藏,状态由active变为inactive。 3.点击页面B中的Button “second page + 2”,Monitor中注册的方法onPageChange会被调用。 4.点击“back”,页面B被销毁,页面A的状态由inactive变为active,对应的Text显示内容改变。 ### TabContent - 对Tabs中当前不可见的TabContent进行冻结,不会触发组件的更新。 - 需要注意的是:在首次渲染的时候,Tab只会创建当前正在显示的TabContent,当切换全部的TabContent后,TabContent才会被全部创建。 ```ts @Entry @ComponentV2 struct TabContentTest { @Local message: number = 0; @Local data: number[] = [0, 1]; build() { Row() { Column() { Button('change message').onClick(() => { this.message++; }) Tabs() { ForEach(this.data, (item: number) => { TabContent() { FreezeChild({ message: this.message, index: item }) }.tabBar(`tab${item}`) }, (item: number) => item.toString()) } } .width('100%') } .height('100%') } } @ComponentV2({ freezeWhenInactive: true }) struct FreezeChild { @Param message: number = 0; @Param index: number = 0; @Monitor('message') onMessageUpdated(mon: IMonitor) { console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`); } build() { Text("message" + `${this.message}, index: ${this.index}`) .fontSize(50) .fontWeight(FontWeight.Bold) } } ``` 在上面的示例中: 1.点击“change message”更改message的值,当前正在显示的TabContent组件中的@Monitor中注册的方法onMessageUpdated被触发。 2.点击TabBar“tab1”切换到另外的TabContent,TabContent状态由inactive变为active,对应的@Monitor中注册的方法onMessageUpdated被触发。 3.再次点击“change message”更改message的值,仅当前显示的TabContent子组件中的@Monitor中注册的方法onMessageUpdated被触发。 ### Navigation - 对当前不可见的页面进行冻结,不会触发组件的更新,当返回该页面时,触发@Monitor回调进行刷新。 ```ts @Entry @ComponentV2 struct MyNavigationTestStack { @Provider('pageInfo') pageInfo: NavPathStack = new NavPathStack(); @Local message: number = 0; @Monitor('message') info() { console.info(`freeze-test MyNavigation message callback ${this.message}`); } @Builder PageMap(name: string) { if (name === 'pageOne') { pageOneStack({ message: this.message }) } else if (name === 'pageTwo') { pageTwoStack({ message: this.message }) } else if (name === 'pageThree') { pageThreeStack({ message: this.message }) } } build() { Column() { Button('change message') .onClick(() => { this.message++; }) Navigation(this.pageInfo) { Column() { Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) .onClick(() => { this.pageInfo.pushPath({ name: 'pageOne' }); //将name指定的NavDestination页面信息入栈 }) } }.title('NavIndex') .navDestination(this.PageMap) .mode(NavigationMode.Stack) } } } @ComponentV2 struct pageOneStack { @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack(); @Local index: number = 1; @Param message: number = 0; build() { NavDestination() { Column() { NavigationContentMsgStack({ message: this.message, index: this.index }) Text("cur stack size:" + `${this.pageInfo.size()}`) .fontSize(30) Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) .onClick(() => { this.pageInfo.pushPathByName('pageTwo', null); }) Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) .onClick(() => { this.pageInfo.pop(); }) }.width('100%').height('100%') }.title('pageOne') .onBackPressed(() => { this.pageInfo.pop(); return true; }) } } @ComponentV2 struct pageTwoStack { @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack(); @Local index: number = 2; @Param message: number = 0; build() { NavDestination() { Column() { NavigationContentMsgStack({ message: this.message, index: this.index }) Text("cur stack size:" + `${this.pageInfo.size()}`) .fontSize(30) Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) .onClick(() => { this.pageInfo.pushPathByName('pageThree', null); }) Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) .onClick(() => { this.pageInfo.pop(); }) } }.title('pageTwo') .onBackPressed(() => { this.pageInfo.pop(); return true; }) } } @ComponentV2 struct pageThreeStack { @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack(); @Local index: number = 3; @Param message: number = 0; build() { NavDestination() { Column() { NavigationContentMsgStack({ message: this.message, index: this.index }) Text("cur stack size:" + `${this.pageInfo.size()}`) .fontSize(30) Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) .height(40) .onClick(() => { this.pageInfo.pushPathByName('pageOne', null); }) Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) .height(40) .onClick(() => { this.pageInfo.pop(); }) } }.title('pageThree') .onBackPressed(() => { this.pageInfo.pop(); return true; }) } } @ComponentV2({ freezeWhenInactive: true }) struct NavigationContentMsgStack { @Param message: number = 0; @Param index: number = 0; @Monitor('message') info() { console.info(`freeze-test NavigationContent message callback ${this.message}`); console.info(`freeze-test ---- called by content ${this.index}`); } build() { Column() { Text("msg:" + `${this.message}`) .fontSize(30) } } } ``` 在上面的示例中: 1.点击“change message”更改message的值,当前正在显示的MyNavigationTestStack组件中的@Monitor中注册的方法info被触发。 2.点击“Next Page”切换到PageOne,创建pageOneStack节点。 3.再次点击“change message”更改message的值,仅pageOneStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。 4.再次点击“Next Page”切换到PageTwo,创建pageTwoStack节点。 5.再次点击“change message”更改message的值,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。 6.再次点击“Next Page”切换到PageThree,创建pageThreeStack节点。 7.再次点击“change message”更改message的值,仅pageThreeStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。 8.点击“Back Page”回到PageTwo,此时,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。 9.再次点击“Back Page”回到PageOne,此时,仅pageOneStack中的NavigationContentMsgStack子组件中的@Monitor中注册的方法info被触发。 10.再次点击“Back Page”回到初始页。