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

相关文章

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模