首页 > 其他分享 >HarmonyOS Next 关于页面渲染的性能优化方案

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

时间:2024-12-15 19:35:01浏览次数:3  
标签:BtnModifier AttributeModifier instance Color son 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 {
  @Builder
  CustomBtn(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 = false
      constructor(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

上述案例中,样式的变更是根据 变量 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: Son
    
      constructor(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',初始值为 false
      isShow: boolean = false;
    }
    
    // 使用 '@Observed' 装饰器标记的类,表示该类的变化可以被观测到
    @Observed
    class Person {
      // 创建一个 'Son' 类型的实例并初始化
      son: Son = new Son();
    }
    
    @Component
    struct CustomBtn {
      // 使用 '@ObjectLink' 装饰器标记的属性,表示与外部对象的链接
      @ObjectLink
      son: 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' 装饰器标记的属性,表示该属性的变化会触发组件的重新渲染
      @State
      person: 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;
  // 表示是否在交谈的布尔属性,初始值为 false
  isTalk: 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 = "人类";
  // 表示是否在交谈的布尔属性,初始值为 false
  isTalk: boolean = false;
}

@Component
struct CustomBtn {
  // 使用 '@ObjectLink' 装饰器标记的属性,表示与外部对象的链接
  @ObjectLink
  person: 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' 装饰器标记的属性,表示该属性的变化会触发组件的重新渲染
  @State
  personList: 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 @Extend AttributeModifier
跨文件导出 不支持 不支持 支持
通用属性设置 支持 支持 支持
通用事件设置 支持 支持 部分支持
组件特有属性设置 不支持 支持 部分支持
组件特有事件设置 不支持 支持 部分支持
参数传递 不支持 支持 支持
多态样式 支持 不支持 支持
业务逻辑 不支持 不支持 支持

基于以上对比,可以看见 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

标签:BtnModifier,AttributeModifier,instance,Color,son,HarmonyOS,属性,Next,页面
From: https://www.cnblogs.com/aspXiaoBai/p/18608369

相关文章

  • HarmonyOS Next 浅谈 发布-订阅模式
    HarmonyOSNext浅谈发布-订阅模式前言其实在目前的鸿蒙应用开发中,或者大前端时代、vue、react、小程序等等框架、语言开发中,普通的使用者越来越少的会碰到必须要掌握设计模式的场景。大白话意思就是一些框架封装太好了,使用者只管在它们的体系下使用就行,哪怕不懂设计模式,也不妨......
  • HarmonyOS Next 元服务新建到上架全流程
    HarmonyOSNext元服务新建到上架全流程接上篇这篇文章的主要目的是介绍元服务从新建到上家的完整流程在AGC平台上新建一个项目链接一个项目可以多个应用AGC新建一个元服务应用新建一个本地元服务项目如果成功在AGC平台上新建过元服务,那么这里会自动显示修改元服务......
  • HarmonyOS Next 简单上手元服务开发
    HarmonyOSNext简单上手元服务开发万物互联时代,人均持有设备量不断攀升,设备种类和使用场景更加多样,使得应用开发、应用入口变得更加复杂。在此背景下,应用提供方和用户迫切需要一种新的服务提供方式,使应用开发更简单、服务(如听音乐、打车等)的获取和使用更便捷。为此,HarmonyOS除......
  • HarmonyOS Next 如何优雅的编写注释
    HarmonyOSNext如何优雅的编写注释程序员箴言我最讨厌世界上的两种人:第一种是不写注释的人第二种是让我写注释的人前言随着HarmonyOSNEXT的发展加快,不少的公司已经陆续加大了资源来开发软件项目。那么伴随项目的发展,项目团队也需要按照一定的规范来编写项目注释或者......
  • HarmonyOS Next 开发的艺术 面向对象
    HarmonyOSNext开发的艺术面向对象聊聊应用的开发范式Harmony应用的主要开发范式,是采用了类似面向对象的范式。struct在java语言中表示结构体。前端工程师可以将其看成是class即可其实这个开发风格会给大部分的前端工程师不太适应的感觉。因为目前主流的前端开发框架......
  • HarmonyOS Next 实战卡片开发 01
    HarmonyOSNext实战卡片开发01介绍FormKit(卡片开发服务)提供一种界面展示形式,可以将应用的重要信息或操作前置到服务卡片(以下简称“卡片”),以达到服务直达、减少跳转层级的体验效果。卡片常用于嵌入到其他应用(当前被嵌入方即卡片使用方只支持系统应用,例如桌面)中作为其界面显示......
  • HarmonyOS Next 设计模式-单例模式
    HarmonyOSNext设计模式-单例模式前言软件设计模式([Designpattern](https://baike.baidu.com/item/Designpattern/10186718?fromModule=lemma_inlink)),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代......
  • HarmonyOS Next 实战卡片开发 02
    HarmonyOSNext实战卡片开发02卡片开发中,还有一个难点是显示图片。其中分为显示本地图片和显示网络图片显示本地图片卡片可以显示本地图片,如存放在应用临时目录下的图片。路径比如/data/app/el2/100/base/你的项目boundleName/temp/123.png以下操作是为了得到一张该目......
  • HarmonyOS Next 并发 taskpool 和 worker
    HarmonyOSNext并发taskpool和worker总览介绍并发,指的是同一时间内,多段代码同时执行。在ArkTs编程中,并发分为异步并发和多线程并发。异步并发异步并发并不是真正的并发,比如在单核设备中,同时执行多端代码其实是通过CPU快速调度来实现的。比如一个司机,它在同一时间只......
  • 鸿蒙UI系统组件12——页面间跳转(router)
    1、概述实际的APP开发中,几乎都会遇到有多个页面跳转的情况,例如,登录->首页->个人中心。在鸿蒙开发中,页面间的跳转被称作为“页面路由”。HarmonyOS提供了Router模块,通过不同的url地址,可以方便地进行页面路由,轻松地访问不同的页面。本文将从页面跳转、页面返回和页面返回前增......