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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�