HarmonyOS开发实战( Beta5.0)自定义组件冻结功能规范

本文主要是介绍HarmonyOS开发实战( Beta5.0)自定义组件冻结功能规范,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

自定义组件处于非激活状态时,状态变量将不响应更新,即@Watch不会调用,状态变量关联的节点不会刷新。通过freezeWhenInactive属性来决定是否使用冻结功能,不传参数时默认不使用。支持的场景有:页面路由,TabContent,LazyForEach,Navigation。

说明:

从API version 11开始,支持自定义组件冻结功能。

当前支持的场景

页面路由

  • 当页面A调用router.pushUrl接口跳转到页面B时,页面A为隐藏不可见状态,此时如果更新页面A中的状态变量,不会触发页面A刷新。

  • 当应用退到后台运行时无法被冻结。

页面A:

import { router } from '@kit.ArkUI';@Entry
@Component({ freezeWhenInactive: true })
struct FirstTest {@StorageLink('PropA') @Watch("first") storageLink: number = 47;first() {console.info("first page " + `${this.storageLink}`)}build() {Column() {Text(`From fist Page ${this.storageLink}`).fontSize(50)Button('first page storageLink + 1').fontSize(30).onClick(() => {this.storageLink += 1})Button('go to next page').fontSize(30).onClick(() => {router.pushUrl({ url: 'pages/second' })})}}
}

页面B:

import { router } from '@kit.ArkUI';@Entry
@Component({ freezeWhenInactive: true })
struct SecondTest {@StorageLink('PropA') @Watch("second") storageLink2: number = 1;second() {console.info("second page: " + `${this.storageLink2}`)}build() {Column() {Text(`second Page ${this.storageLink2}`).fontSize(50)Button('Change Divider.strokeWidth').onClick(() => {router.back()})Button('second page storageLink2 + 2').fontSize(30).onClick(() => {this.storageLink2 += 2})}}
}

在上面的示例中:

1.点击页面A中的Button “first page storageLink + 1”,storageLink状态变量改变,@Watch中注册的方法first会被调用。

2.通过router.pushUrl({url: 'pages/second'}),跳转到页面B,页面A隐藏,状态由active变为inactive。

3.点击页面B中的Button “this.storageLink2 += 2”,只回调页面B@Watch中注册的方法second,因为页面A的状态变量此时已被冻结。

4.点击“back”,页面B被销毁,页面A的状态由inactive变为active,重新刷新在inactive时被冻结的状态变量,页面A@Watch中注册的方法first被再次调用。

TabContent

  • 对Tabs中当前不可见的TabContent进行冻结,不会触发组件的更新。

  • 需要注意的是:在首次渲染的时候,Tab只会创建当前正在显示的TabContent,当切换全部的TabContent后,TabContent才会被全部创建。

@Entry
@Component
struct TabContentTest {@State @Watch("onMessageUpdated") message: number = 0;private data: number[] = [0, 1]onMessageUpdated() {console.info(`TabContent message callback func ${this.message}`)}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%')}
}@Component({ freezeWhenInactive: true })
struct FreezeChild {@Link @Watch("onMessageUpdated") message: numberprivate index: number = 0onMessageUpdated() {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组件中的@Watch中注册的方法onMessageUpdated被触发。

2.点击“two”切换到另外的TabContent,TabContent状态由inactive变为active,对应的@Watch中注册的方法onMessageUpdated被触发。

3.再次点击“change message”更改message的值,仅当前显示的TabContent子组件中的@Watch中注册的方法onMessageUpdated被触发。

TabContent.gif

LazyForEach

  • 对LazyForEach中缓存的自定义组件进行冻结,不会触发组件的更新。
// Basic implementation of IDataSource to handle data listener
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = [];private originDataArray: string[] = [];public totalCount(): number {return 0;}public getData(index: number): string {return this.originDataArray[index];}// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener');this.listeners.push(listener);}}// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('remove listener');this.listeners.splice(pos, 1);}}// 通知LazyForEach组件需要重载所有子组件notifyDataReload(): void {this.listeners.forEach(listener => {listener.onDataReloaded();})}// 通知LazyForEach组件需要在index对应索引处添加子组件notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index);})}// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index);})}// 通知LazyForEach组件需要在index对应索引处删除该子组件notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index);})}
}class MyDataSource extends BasicDataSource {private dataArray: string[] = [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public addData(index: number, data: string): void {this.dataArray.splice(index, 0, data);this.notifyDataAdd(index);}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}@Entry
@Component
struct LforEachTest {private data: MyDataSource = new MyDataSource();@State @Watch("onMessageUpdated") message: number = 0;onMessageUpdated() {console.info(`LazyforEach message callback func ${this.message}`)}aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {Column() {Button('change message').onClick(() => {this.message++})List({ space: 3 }) {LazyForEach(this.data, (item: string) => {ListItem() {FreezeChild({ message: this.message, index: item })}}, (item: string) => item)}.cachedCount(5).height(500)}}
}@Component({ freezeWhenInactive: true })
struct FreezeChild {@Link @Watch("onMessageUpdated") message: number;private index: string = "";aboutToAppear() {console.info(`FreezeChild aboutToAppear index: ${this.index}`)}onMessageUpdated() {console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`)}build() {Text("message" + `${this.message}, index: ${this.index}`).width('90%').height(160).backgroundColor(0xAFEEEE).textAlign(TextAlign.Center).fontSize(30).fontWeight(FontWeight.Bold)}
}

在上面的示例中:

1.点击“change message”更改message的值,当前正在显示的ListItem中的子组件@Watch中注册的方法onMessageUpdated被触发。缓存节点@Watch中注册的方法不会被触发。(如果不加组件冻结,当前正在显示的ListItem和cachecount缓存节点@Watch中注册的方法onMessageUpdated都会触发watch回调。)

2.List区域外的ListItem滑动到List区域内,状态由inactive变为active,对应的@Watch中注册的方法onMessageUpdated被触发。

3.再次点击“change message”更改message的值,仅有当前显示的ListItem中的子组件@Watch中注册的方法onMessageUpdated被触发。

FrezzeLazyforEach.gif

Navigation

  • 当NavDestination不可见时,会对其子自定义组件设置成非激活态,不会触发组件的刷新。当返回该页面时,其子自定义组件重新恢复成激活态,触发@Watch回调进行刷新。

  • 在下面例子中,NavigationContentMsgStack会被设置成非激活态,将不再响应状态变量的变化,也不会触发组件刷新。

@Entry
@Component
struct MyNavigationTestStack {@Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();@State @Watch("info") message: number = 0;@State logNumber: number = 0;info() {console.info(`freeze-test MyNavigation message callback ${this.message}`);}@BuilderPageMap(name: string) {if (name === 'pageOne') {pageOneStack({ message: this.message, logNumber: this.logNumber })} else if (name === 'pageTwo') {pageTwoStack({ message: this.message, logNumber: this.logNumber })} else if (name === 'pageThree') {pageThreeStack({ message: this.message, logNumber: this.logNumber })}}build() {Column() {Button('change message').onClick(() => {this.message++;})Navigation(this.pageInfo) {Column() {Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPath({ name: 'pageOne' }); //将name指定的NavDestination页面信息入栈})}}.title('NavIndex').navDestination(this.PageMap).mode(NavigationMode.Stack)}}
}@Component
struct pageOneStack {@Consume('pageInfo') pageInfo: NavPathStack;@State index: number = 1;@Link message: number;@Link logNumber: number;build() {NavDestination() {Column() {NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })Text("cur stack size:" + `${this.pageInfo.size()}`).fontSize(30).fontWeight(FontWeight.Bold)Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPathByName('pageTwo', null);})Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pop();})}.width('100%').height('100%')}.title('pageOne').onBackPressed(() => {this.pageInfo.pop();return true;})}
}@Component
struct pageTwoStack {@Consume('pageInfo') pageInfo: NavPathStack;@State index: number = 2;@Link message: number;@Link logNumber: number;build() {NavDestination() {Column() {NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })Text("cur stack size:" + `${this.pageInfo.size()}`).fontSize(30).fontWeight(FontWeight.Bold)Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPathByName('pageThree', null);})Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pop();})}.width('100%').height('100%')}.title('pageTwo').onBackPressed(() => {this.pageInfo.pop();return true;})}
}@Component
struct pageThreeStack {@Consume('pageInfo') pageInfo: NavPathStack;@State index: number = 3;@Link message: number;@Link logNumber: number;build() {NavDestination() {Column() {NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })Text("cur stack size:" + `${this.pageInfo.size()}`).fontSize(30).fontWeight(FontWeight.Bold)Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pushPathByName('pageOne', null);})Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {this.pageInfo.pop();})}.width('100%').height('100%')}.title('pageThree').onBackPressed(() => {this.pageInfo.pop();return true;})}
}@Component({ freezeWhenInactive: true })
struct NavigationContentMsgStack {@Link @Watch("info") message: number;@Link index: number;@Link logNumber: number;info() {console.info(`freeze-test NavigationContent message callback ${this.message}`);console.info(`freeze-test ---- called by content ${this.index}`);this.logNumber++;}build() {Column() {Text("msg:" + `${this.message}`).fontSize(30).fontWeight(FontWeight.Bold)Text("log number:" + `${this.logNumber}`).fontSize(30).fontWeight(FontWeight.Bold)}}
}

在上面的示例中:

1.点击“change message”更改message的值,当前正在显示的MyNavigationTestStack组件中的@Watch中注册的方法info被触发。

2.点击“Next Page”切换到PageOne,创建pageOneStack节点。

3.再次点击“change message”更改message的值,仅pageOneStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

4.再次点击“Next Page”切换到PageTwo,创建pageTwoStack节点。

5.再次点击“change message”更改message的值,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

6.再次点击“Next Page”切换到PageThree,创建pageThreeStack节点。

7.再次点击“change message”更改message的值,仅pageThreeStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

8.点击“Back Page”回到PageTwo,此时,仅pageTwoStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

9.再次点击“Back Page”回到PageOne,此时,仅pageOneStack中的NavigationContentMsgStack子组件中的@Watch中注册的方法info被触发。

10.再次点击“Back Page”回到初始页,此时,无任何触发。

navigation-freeze.gif

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:

GitCode - 全球开发者的开源社区,开源代码托管平台  希望这一份鸿蒙学习文档能够给大家带来帮助~


鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习教程+学习PDF文档

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习文档(面试、文档、全套视频等)       

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

这篇关于HarmonyOS开发实战( Beta5.0)自定义组件冻结功能规范的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139587

相关文章

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

Vue3视频播放组件 vue3-video-play使用方式

《Vue3视频播放组件vue3-video-play使用方式》vue3-video-play是Vue3的视频播放组件,基于原生video标签开发,支持MP4和HLS流,提供全局/局部引入方式,可监听... 目录一、安装二、全局引入三、局部引入四、基本使用五、事件监听六、播放 HLS 流七、更多功能总结在 v

使用EasyPoi快速导出Word文档功能的实现步骤

《使用EasyPoi快速导出Word文档功能的实现步骤》EasyPoi是一个基于ApachePOI的开源Java工具库,旨在简化Excel和Word文档的操作,本文将详细介绍如何使用EasyPoi快速... 目录一、准备工作1、引入依赖二、准备好一个word模版文件三、编写导出方法的工具类四、在Export

Python爬虫HTTPS使用requests,httpx,aiohttp实战中的证书异步等问题

《Python爬虫HTTPS使用requests,httpx,aiohttp实战中的证书异步等问题》在爬虫工程里,“HTTPS”是绕不开的话题,HTTPS为传输加密提供保护,同时也给爬虫带来证书校验、... 目录一、核心问题与优先级检查(先问三件事)二、基础示例:requests 与证书处理三、高并发选型:

JS纯前端实现浏览器语音播报、朗读功能的完整代码

《JS纯前端实现浏览器语音播报、朗读功能的完整代码》在现代互联网的发展中,语音技术正逐渐成为改变用户体验的重要一环,下面:本文主要介绍JS纯前端实现浏览器语音播报、朗读功能的相关资料,文中通过代码... 目录一、朗读单条文本:① 语音自选参数,按钮控制语音:② 效果图:二、朗读多条文本:① 语音有默认值:②

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

Java中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例解析

《Java中的分布式系统开发基于Zookeeper与Dubbo的应用案例解析》本文将通过实际案例,带你走进基于Zookeeper与Dubbo的分布式系统开发,本文通过实例代码给大家介绍的非常详... 目录Java 中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例一、分布式系统中的挑战二

C#实现高性能拍照与水印添加功能完整方案

《C#实现高性能拍照与水印添加功能完整方案》在工业检测、质量追溯等应用场景中,经常需要对产品进行拍照并添加相关信息水印,本文将详细介绍如何使用C#实现一个高性能的拍照和水印添加功能,包含完整的代码实现... 目录1. 概述2. 功能架构设计3. 核心代码实现python3.1 主拍照方法3.2 安全HBIT