HarmonyOS Next 关于页面渲染的性能优化方案

HarmonyOS Next 关于页面渲染的性能优化方案

HarmonyOS Next 应用开发中,用户的使用体验至关重要。其中用户启动APP到呈现页面主要包含三个步骤:

  1. 框架初始化
  2. 页面加载
  3. 布局渲染

image-20241030202356771

页面加载布局渲染中,主要包含了6个环节:

  1. 执行页面文件
  2. 生成页面节点树
  3. 页面节点树挂载
  4. 布局
  5. 渲染
  6. 展示

页面节点树挂载的速度取决于节点的数量,我们可以理解给1个自定义组件在渲染时,后端同时会生成一个对应的

节点。该节点后期会用来diff

渲染的速度取决于布局属性。如果布局属性越复杂、冗余。那么就越慢。

节点的数量优化

HarmonyOS Next 会根据自定义节点的数量在后端生成对应的节点。那么如果我们在实际开发中,可以考虑尽量的将自定义组件的数量减少,替换成 @Builder 自定义构建函数。

那么哪些自定义节点可以替换成**@Builder**自定义构建函数呢,看下表:

分类自定义组件@Builder
复用布局结构支持支持
复用样式支持支持
导出使用支持不支持
生命周期支持不支持
状态管理支持不支持

所以,当我们对于封装的需求,不需要导出使用、不需要使用生命周期、不需要独立的状态管理时。就可以使用**@Builder**来代替自定义组件。

@Builder的基本使用

image-20241030204135638

@Entry
@Component
struct Index {@BuilderCustomBtn(text:string){Button(text).width(100).height(50).linearGradient({colors:[[Color.Black,0],[Color.Red,1]]})}build() {Column({space:10}){this.CustomBtn("登录")this.CustomBtn("注册")}.width("100%").height("100%").justifyContent(FlexAlign.Center)}
}

自定义组件的基本使用

image-20241030204135638

@Component
struct CustomBtn {text: string = ""build() {Button(this.text).width(100).height(50).linearGradient({colors: [[Color.Black, 0], [Color.Red, 1]]})}
}@Entry
@Component
struct Index {build() {Column({ space: 10 }) {CustomBtn({text:"登录"})CustomBtn({text:"注册"})}.width("100%").height("100%").justifyContent(FlexAlign.Center)}
}

布局属性的优化

这里的优化,主要是指性能的优化,也就是用户体验的优化,不是对于开发者来讲的开发体验的优化。

HarmonyOS Next 有提供 @Styles@Extends 来实现代码层面的优化,也就是样式代码的简单封装。

但是无论是用户层面的优化和代码层面的优化。@Styles 和 @Extends 都存在一定的限制。因此HarmonyOS

Next 又推出了 AttributeModifierAttributeUpdater(AttributeUpdater 是AttributeModifier的继承 )

AttributeModifier

  1. AttributeModifier是一个接口,需要我们主动实现它相关的方法。如默认态(Normal)、按压态(Pressed)、焦点态(Focused)、禁用态(Disabled)、选择态(Selected)
  2. AttributeModifier 可以实现样式属性的按需注册
  3. 支持和@Observed和@ObjectLink配套使用

AttributeModifier 基本使用

  1. 定义MyButtonModifier类,继承AttributeModifier接口,并且声明是对Button进行的样式属性封装

    export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {
    
  2. MyButtonModifier中声明变量,用来注册不同的样式属性

    isDark: boolean = false
    
  3. 定义正常态的样式 (applyNormalAttribute 是接口AttributeModifier中定义的 )

      applyNormalAttribute(instance: ButtonAttribute): void {if (this.isDark) {instance.backgroundColor(Color.Black).fontColor(Color.White).border({width:10,color:Color.Brown}).borderRadius(20).padding(10).margin(20)} else {instance.backgroundColor(Color.White).fontColor(Color.Black)}}
    

    image-20241030221519493

  4. 组件中开始复用(完整代码)

    export class MyButtonModifier implements AttributeModifier<ButtonAttribute> {isDark: boolean = falseconstructor(dark?: boolean) {this.isDark = !!dark}applyNormalAttribute(instance: ButtonAttribute): void {if (this.isDark) {instance.backgroundColor(Color.Black).fontColor(Color.White).border({width:10,color:Color.Brown}).borderRadius(20).padding(10).margin(20)} else {instance.backgroundColor(Color.White).fontColor(Color.Black)}}
    }@Entry
    @Component
    struct attributeDemo {@State modifier: MyButtonModifier = new MyButtonModifier(false);build() {Row() {Column() {Button("Button")// 注册属性.attributeModifier(this.modifier).onClick(() => {// 点击切换this.modifier.isDark = !this.modifier.isDark})}.width('100%')}.height('100%')}
    }
    
  5. 效果

    PixPin_2024-10-30_22-18-52

AttributeModifier 其他状态

多态样式中除了默认态(Normal)还有 、按压态(Pressed)、焦点态(Focused)、禁用态(Disabled)、选择态(Selected)。我们一并实现。

  // 按压applyPressedAttribute(instance: ButtonAttribute): void {instance.backgroundColor(Color.Red)}// 获得焦点applyFocusedAttribute(instance: ButtonAttribute): void {}// 选择applySelectedAttribute(instance: ButtonAttribute): void {}// 禁用applyDisabledAttribute(instance: ButtonAttribute): void {}

PixPin_2024-10-30_22-25-22

搭配 @Observed和@ObjectLink

上述案例中,样式的变更是根据 变量 isDark来实现的。如果想要根据对象中某个属性来实现样式的变更。我们可以搭配@Observed和@ObjectLink

对象嵌套对象

以下代码主要利用了 @Observed和@ObjectLink 可以监听深层次属性的改变,然后当深层次属性改变后,触发

AttributeModifier 跟随改变。

  1. 声明子类 Son,代表深层次属性的载体

    @Observed
    class Son {// 控制样式切换的关键变量isShow: boolean = false
    }
    
  2. 使用 @Observed 修饰 Person类 (父类),拥有Son子类

    @Observed
    class Person {son: Son = new Son()
    }
    
  3. 声明 BtnModifier 类,需要实现 AttributeModifier 接口,实现样式优化和复用。接收 son属性。用来响应状态变化

    class BtnModifier implements AttributeModifier<ButtonAttribute> {son: Sonconstructor(son: Son) {this.son = son}applyNormalAttribute(instance: ButtonAttribute): void {if (this.son.isShow) {instance.backgroundColor(Color.Red)} else {instance.backgroundColor(Color.Green)}}
    }
    
  4. 完整代码

    import { promptAction } from '@kit.ArkUI';// 定义一个名为 'BtnModifier' 的类,实现对 'ButtonAttribute' 的属性修改
    class BtnModifier implements AttributeModifier<ButtonAttribute> {// 存储一个 'Son' 类型的实例son: Son;// 构造函数,接收一个 'Son' 类型的参数并初始化 'son' 属性constructor(son: Son) {this.son = son;}// 应用普通属性的方法,接收一个 'ButtonAttribute' 类型的实例作为参数applyNormalAttribute(instance: ButtonAttribute): void {// 如果 'son' 的 'isShow' 属性为 true,则将按钮背景颜色设置为红色if (this.son.isShow) {instance.backgroundColor(Color.Red);} else {// 否则将按钮背景颜色设置为绿色instance.backgroundColor(Color.Green);}}
    }// 使用 '@Observed' 装饰器标记的类,表示该类的变化可以被观测到
    @Observed
    class Son {// 定义一个布尔类型的属性 'isShow',初始值为 falseisShow: boolean = false;
    }// 使用 '@Observed' 装饰器标记的类,表示该类的变化可以被观测到
    @Observed
    class Person {// 创建一个 'Son' 类型的实例并初始化son: Son = new Son();
    }@Component
    struct CustomBtn {// 使用 '@ObjectLink' 装饰器标记的属性,表示与外部对象的链接@ObjectLinkson: Son;// 可空的 'BtnModifier' 类型属性modify: BtnModifier | null = null;// 在组件即将出现时执行的方法aboutToAppear(): void {// 创建一个新的 'BtnModifier' 实例并赋值给 'modify' 属性this.modify = new BtnModifier(this.son);}// 构建组件的方法build() {// 创建一个按钮,并将按钮的文本设置为 'son.isShow' 的字符串表示形式Button(this.son.isShow.toString())// 设置按钮的属性修改器为 'modify'.attributeModifier(this.modify);}
    }@Entry
    @Component
    struct Index {// 使用 '@State' 装饰器标记的属性,表示该属性的变化会触发组件的重新渲染@Stateperson: Person = new Person();build() {// 创建一个列容器Column() {// 创建一个自定义按钮组件,并传入 'person.son' 作为参数CustomBtn({ son: this.person.son })// 为按钮添加点击事件处理函数.onClick(() => {// 切换 'person.son.isShow' 的值this.person.son.isShow = !this.person.son.isShow;// 显示一个提示信息promptAction.showToast({ message: `${this.person.son.isShow}` });});}.width("100%").height("100%").justifyContent(FlexAlign.Center);}
    }

    PixPin_2024-10-30_23-36-42

数组嵌套对象

数组嵌套对象的写法类似上面示例,但是可以通过简单的一些编程技巧来进一步优化。如单例

// 定义一个名为 'BtnModifier' 的类,实现对 'ButtonAttribute' 的属性修改
class BtnModifier implements AttributeModifier<ButtonAttribute> {// 静态变量,用于存储单例实例static instance: BtnModifier;// 表示是否在交谈的布尔属性,初始值为 falseisTalk: boolean = false;// 静态方法,用于获取单例实例static getInstance(): BtnModifier {// 如果单例实例不存在,则创建一个新的实例if (!BtnModifier.instance) {BtnModifier.instance = new BtnModifier();}// 返回单例实例return BtnModifier.instance;}// 设置 isTalk 属性的方法setTalk(isTalk: boolean): BtnModifier {// 更新 isTalk 属性值this.isTalk = isTalk;// 返回当前实例,以便进行链式调用return this;}// 应用普通属性的方法,接收一个 'ButtonAttribute' 类型的实例作为参数applyNormalAttribute(instance: ButtonAttribute): void {// 如果 isTalk 为 true,则将按钮背景颜色设置为红色if (this.isTalk) {instance.backgroundColor(Color.Red);} else {// 否则将按钮背景颜色设置为绿色instance.backgroundColor(Color.Green);}}
}// 使用 '@Observed' 装饰器标记的类,表示该类的变化可以被观测到
@Observed
class Person {// 用户名属性,初始值为 "人类"userName: string = "人类";// 表示是否在交谈的布尔属性,初始值为 falseisTalk: boolean = false;
}@Component
struct CustomBtn {// 使用 '@ObjectLink' 装饰器标记的属性,表示与外部对象的链接@ObjectLinkperson: Person;// 存储 'BtnModifier' 的实例,通过单例模式获取modify: BtnModifier = BtnModifier.getInstance();// 构建组件的方法build() {// 创建一个按钮,并将按钮的文本设置为 'person.userName'Button(this.person.userName)// 设置按钮的属性修改器,并根据 'person.isTalk' 的值设置是否在交谈状态.attributeModifier(this.modify.setTalk(this.person.isTalk));}
}@Entry
@Component
struct Index {// 使用 '@State' 装饰器标记的属性,表示该属性的变化会触发组件的重新渲染@StatepersonList: Person[] = [new Person(), new Person()];// 构建组件的方法build() {// 创建一个列容器Column() {// 遍历 'personList',为每个 'Person' 实例创建一个 'CustomBtn' 组件,并添加点击事件处理函数ForEach(this.personList, (person: Person) => {CustomBtn({ person: person }).onClick(() => {// 切换 'person.isTalk' 的值person.isTalk = !person.isTalk;});});}.width("100%").height("100%").justifyContent(FlexAlign.Center);}
}

PixPin_2024-10-30_23-39-43

AttributeModifier 和 @Styles 、@Extend的比较

能力@Styles@ExtendAttributeModifier
跨文件导出不支持不支持支持
通用属性设置支持支持支持
通用事件设置支持支持部分支持
组件特有属性设置不支持支持部分支持
组件特有事件设置不支持支持部分支持
参数传递不支持支持支持
多态样式支持不支持支持
业务逻辑不支持不支持支持

基于以上对比,可以看见 AttributeModifier 几乎可以满足以上所有场景。唯一缺点就是代码量稍多一些些。

接口定义

declare interface AttributeModifier<T> {applyNormalAttribute?(instance: T): void;applyPressedAttribute?(instance: T): void;applyFocusedAttribute?(instance: T): void;applyDisabledAttribute?(instance: T): void;applySelectedAttribute?(instance: T): void;}

AttributeUpdater

如果设计大量的样式属性修改,如果都是基于状态变量,那么在实现修改前,还是会导致diff的对比,性能损耗验证。因此引入了 AttributeUpdater,它继承了AttributeModifier基本能力,还拓展了直接修改属性和组件构造函数的能力。用来根据单一状态来批量修改样式属性。

简单实用

  1. 声明 MyButtonUpdater 类,继承 AttributeUpdater
  2. 组件中实例化 MyButtonUpdater
  3. 直接修改组件样式属性
import { AttributeUpdater } from '@kit.ArkUI';// 注意,这里是继承  AttributeUpdater 类
class MyButtonUpdater extends AttributeUpdater<ButtonAttribute> {
}@Entry
@Component
struct attributeDemo {@State modifier: MyButtonUpdater = new MyButtonUpdater();build() {Row() {Column() {Button("直接修改批量样式属性").attributeModifier(this.modifier).onClick(() => {// 直接修改this.modifier.attribute?.backgroundColor(Color.Green).width(200).fontColor(Color.Red)})}.width('100%')}.height('100%')}
}

PixPin_2024-10-30_23-53-22

重新调用组件构造函数

提供了updateConstructorParams 接口,可以让我们重新调用该组件的构造函数。实现组件的重新渲染

  1. 继承 AttributeUpdater 类时,同时传入两个泛型 ButtonAttributeButtonInterface

    class MyButtonUpdater extends AttributeUpdater<ButtonAttribute,ButtonInterface> {}
    
  2. 直接调用要组件的构造函数 updateConstructorParams

    import { AttributeUpdater } from '@kit.ArkUI';// 注意,这里是继承  AttributeUpdater 类
    class MyButtonUpdater extends AttributeUpdater<ButtonAttribute,ButtonInterface> {}@Entry
    @Component
    struct attributeDemo {@State modifier: MyButtonUpdater = new MyButtonUpdater();build() {Row() {Column() {Button("重新渲染组件").attributeModifier(this.modifier).onClick(() => {this.modifier.updateConstructorParams("文本也可以改变")})}.width('100%')}.height('100%')}
    }
    

    PixPin_2024-10-31_00-00-34

接口定义

export declare class AttributeUpdater<T, C = Initializer<T>> implements AttributeModifier<T> {applyNormalAttribute?(instance: T): void;initializeModifier(instance: T): void;get attribute(): T | undefined;updateConstructorParams: C;
}

总结

后期如果要考虑实现样式复用,可以优先使用 AttributeModifier 和 AttributeUpdater

image-20241031000618657

作者

作者:万少

链接:https://www.nutpi.net/

來源:坚果派 著作权归作者所有。

商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/20564.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

已解决centos7 yum报错:cannot find a valid baseurl for repo:base/7/x86_64的解决方案

出现cannot find a valid baseurl for repo:base/7/x86_64错误通常是由于YUM仓库源无法找到或无法访问&#xff0c;导致YUM无法正常工作。这种情况常见于CentOS 7系统。解决这个问题需要检查几个方面&#xff0c;如网络连接、DNS设置和YUM仓库源配置。 &#x1f9d1; 博主简介&…

架构图解析:如何构建高效的微服务系统

在当今的数字化浪潮中&#xff0c;构建高效、灵活且可扩展的系统已成为企业的重要目标。微服务架构作为一种先进的软件设计模式&#xff0c;通过将复杂的应用程序分解为一系列小型、独立的服务&#xff0c;显著提升了系统的灵活性、可扩展性和维护性。本文将通过解析微服务系统…

Label-studio-ml-backend 和YOLOV8 YOLO11自动化标注,目标检测,实例分割,图像分类,关键点估计,视频跟踪

这里写目录标题 1.目标检测 Detection2.实例分割 segment3.图像分类 classify4.关键点估计 Keypoint detection5.视频帧检测 video detect6.视频帧分类 video classify7.旋转目标检测 obb detect8.替换yolo11模型 给我点个赞吧&#xff0c;谢谢了附录coco80类名称 笔记本 华为m…

恒利联创携手Pearson VUE 亮相第62届高博会

2024年11月15日-17日&#xff0c;第62届中国高等教育博览会&#xff08;简称“高博会”&#xff09;在重庆举行&#xff0c;恒利联创携手全球领先的考试服务提供商Pearson Vue Certiport共同亮相&#xff0c;为中国院校展现并提供数字化职业技能的教育平台及学练考体系。 作为P…

linux复习2:简单命令简述

cp 复制单个文件 cp file.txt /path/to/destination/ 将 file.txt 复制到指定的目标目录。 复制多个文件 cp file1.txt file2.txt /path/to/destination/ 将 file1.txt 和 file2.txt 复制到指定的目标目录。 复制目录&#xff08;递归复制&#xff09; cp -r /path/to/source…

【逆向篇】抓取微信小程序源码 (附加逆向工具wxappUnpacker和使用方法)

抓取微信小程序源码附加逆向工具wxappUnpacker 文章目录前言一、工具准备1 解密工具2 逆向工具 二、解密小程序1.确认小程序包位置2.打开一个小程序3.解密小程序包 三、逆向小程序1、检查nodejs2、安装依赖3、正式逆向 该文章只是学习作用&#xff0c;如果侵权请联系删除&…

【C++】拷贝构造

一种特殊的构造函数&#xff0c;用自身这种类型来构造自身 Student stu1; Student stu2stu1;//调用拷贝构造如果类中没有自定义拷贝构造&#xff0c;类中会自动提供一个默认拷贝构造如果类中定义了自定义拷贝构造&#xff0c;类中不会提供默认拷贝构造 自定义拷贝构造 类名(…

C++的IO流

目录 1. C语言的输入与输出 2. 流是什么 3. CIO流 3.1 C标准IO流 3.2 C文件IO流 4 stringstream的简单介绍 1. 将数值类型数据格式化为字符串 2. 字符串拼接 3. 序列化和反序列化结构数据 1. C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。…

青训营刷题笔记11

水一个简单题&#xff1a; 问题描述 小C定义了一个“完美偶数”。一个正整数 xx 被认为是完美偶数需要满足以下两个条件&#xff1a; xx 是偶数&#xff1b;xx 的值在区间 [l,r][l,r] 之间。 现在&#xff0c;小C有一个长度为 nn 的数组 aa&#xff0c;她想知道在这个数组中…

游戏+AI的发展历程,AI技术在游戏行业的应用有哪些?

人工智能&#xff08;AI&#xff09;与游戏的结合&#xff0c;不仅是技术进步的体现&#xff0c;更是人类智慧的延伸。从最初的简单规则到如今的复杂决策系统&#xff0c;AI在游戏领域的发展历史可谓波澜壮阔。 早在2001年&#xff0c;就有研究指出游戏人工智能领域&#xff0…

Vue.js 插槽 Slots 实际应用 最近重构项目的时候遇到的...

前端开发中 插槽 Slots 是一个重要的概念 我们可以查看一下vue.js的官方文档 https://cn.vuejs.org/guide/components/slots 类似于连接通道一样 可以把核心代码逻辑搬到另外的地方 做一个引用 而原先的地方可能并不能这样书写 对于这个概念我在vue的官方文档里面找到了…

Windows11在WSL中安装QEMU-KVM

Windows11在WSL中安装QEMU-KVM 检查系统信息WSL检测安装所需软件端口转发 检查系统信息 打开设置-系统-系统信息&#xff08;拉到最下面&#xff09;&#xff0c;我的是 版本 Windows 11 专业版 版本号 24H2 安装日期 ‎2024/‎11/‎13 操作系统版本 26100.2314 体验 Windows …

【东莞石碣】戴尔R740服务器维修raid硬盘问题

1&#xff1a;石碣某塑料工厂下午报修一台戴尔R740服务器硬盘故障&#xff0c;催的还比较着急。 2&#xff1a;工程师经过跟用户确认故障的问题以及故障服务器型号和故障硬盘型号&#xff0c;产品和配件确认好后&#xff0c;公司仓库确认有该款硬盘现货&#xff0c;DELL 12T S…

SpringBoot学习笔记(一)

一、Spring Boot概述 &#xff08;一&#xff09;微服务概述 1、微服务 微服务&#xff08;英语&#xff1a;Microservices&#xff09;是一种软件架构风格&#xff0c;它是以专注于单一责任与功能的小型功能区块 (Small Building Blocks) 为基础&#xff0c;利用模块化的方式…

SD模型微调之LoRA

​ &#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&a…

手机远程控制电脑,让办公更快捷

在数字化办公的浪潮下&#xff0c;远程控制软件已成为连接工作与生活的桥梁。它使得用户能够通过一台设备&#xff08;主控端&#xff09;来操作另一台设备&#xff08;被控端&#xff09;&#xff0c;无论它们是否位于同一局域网内。这种软件广泛应用于远程办公、手机远程控制…

【Three.js基础学习】26. Animated galaxy

前言 shaders实现星系 课程回顾 使用顶点着色器为每个粒子设置动画 a属性 &#xff0c; u制服 &#xff0c;v变化 像素比&#xff1a;window.devicePixelRatio 自动从渲染器检索像素比 renderer.getPixelRatio() 如何尺寸衰减&#xff0c; 放大缩小视角时&#xff0c;粒子都是同…

基于Springboot + Vue的旧物置换网站管理系统(源码+lw+部署讲解+PPT)

前言 详细视频演示 论文参考 系统介绍 系统概述 核心功能 具体实现截图 1. 首页功能 2. 旧物信息功能 3. 网站公告功能 4. 用户管理功能&#xff08;管理员端&#xff09; 5. 置换交易管理功能 技术栈 后端框架SpringBoot 前端框架Vue 持久层框架MyBatis-Plus …

新书速览|循序渐进Spark大数据应用开发

《循序渐进Spark大数据应用开发》 本书内容 《循序渐进Spark大数据应用开发》结合作者一线开发实践&#xff0c;循序渐进地介绍了新版Apache Spark 3.x的开发技术。全书共10章&#xff0c;第1章和第2章主要介绍Spark的基本概念、安装&#xff0c;并演示如何编写最简单的Spark程…

一道算法期末应用题及解答

1&#xff0e;印刷电路板布线区划分成为n m 个方格&#xff0c;确定连接方格a 到方格b 的最短布线方案。 在布线时&#xff0c;只能沿直线或者直角布线&#xff0c;为避免交叉&#xff0c;已经布线的方格做了封锁标记&#xff0c;其他线路不允许穿过被封锁的方格&#xff0c;某…