HarmonyOS鸿蒙NEXT关于页面渲染的性能优化方案

HarmonyOS鸿蒙NEXT关于页面渲染的性能优化方案

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

页面渲染过程

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

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

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

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

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

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

节点的数量优化

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

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

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

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

@Builder的基本使用

@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)
  }
}

自定义组件的基本使用

@Component
struct CustomBtn {
  text: string = ""

  build(){
    Button(this.text)
      .width(100)
      .height(50)
      .linearGradient({
        colors: [[Color.Black, 0], [Color.Red, 1]]
      })
  }
}

@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 基本使用

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)
    }
  }
}

@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%')
  }
}

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 {
}

搭配 @Observed@ObjectLink

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

对象嵌套对象

// 定义一个名为 'BtnModifier' 的类,实现对 'ButtonAttribute' 的属性修改
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);
    }
  }
}

@Observed
class Son {
  // 控制样式切换的关键变量
  isShow: boolean = false;
}

@Observed
class Person {
  son: Son = new Son();
}

@Component
struct CustomBtn {
  @ObjectLink
  son: Son;
  modify: BtnModifier | null = null;

  aboutToAppear(): void {
    this.modify = new BtnModifier(this.son);
  }

  build(){
    Button(this.son.isShow.toString())
      // 设置按钮的属性修改器为 'modify'
      .attributeModifier(this.modify);
  }
}

@Component
struct Index {
  @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);
  }
}

数组嵌套对象

// 定义一个名为 'BtnModifier' 的类,实现对 'ButtonAttribute' 的属性修改
class BtnModifier implements AttributeModifier<ButtonAttribute> {
  static instance: BtnModifier;
  isTalk: boolean = false;

  static getInstance(): BtnModifier {
    if (!BtnModifier.instance) {
      BtnModifier.instance = new BtnModifier();
    }
    return BtnModifier.instance;
  }

  setTalk(isTalk: boolean): BtnModifier {
    this.isTalk = isTalk;
    return this;
  }

  applyNormalAttribute(instance: ButtonAttribute): void {
    if (this.isTalk) {
      instance.backgroundColor(Color.Red);
    } else {
      instance.backgroundColor(Color.Green);
    }
  }
}

@Observed
class Person {
  userName: string = "人类";
  isTalk: boolean = false;
}

@Component
struct CustomBtn {
  @ObjectLink
  person: Person;
  modify: BtnModifier = BtnModifier.getInstance();

  build(){
    Button(person.userName)
      // 设置按钮的属性修改器,并根据 'person.isTalk' 的值设置是否在交谈状态
      .attributeModifier(modify.setTalk(person.isTalk));
  }
}

@Component
struct Index {
  @State
  personList: Person[] = [new Person(), new Person()];

  build(){
    Column() {
      ForEach(this.personList, (person: Person) => {
        CustomBtn({ person })
          .onClick(() => {
            // 切换 'person.isTalk' 的值
            person.isTalk = !person.isTalk;
          });
      });
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center);
  }
}

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> {
}

@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%')
  }
}

重新调用组件构造函数

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

  1. 继承 AttributeUpdater 类时,同时传入两个泛型 ButtonAttributeButtonInterface
class MyButtonUpdater extends AttributeUpdater<ButtonAttribute,ButtonInterface> {

}
  1. 直接调用要组件的构造函数 updateConstructorParams
import { AttributeUpdater } from '@kit.ArkUI';

// 注意,这里是继承  AttributeUpdater 类
class MyButtonUpdater extends AttributeUpdater<ButtonAttribute,ButtonInterface> {

}

@Component
struct attributeDemo {
  @State modifier: MyButtonUpdater = new MyButtonUpdater();

  build(){
    Row() {
      Column() {
        Button("重新渲染组件")
          .attributeModifier(this.modifier)
          .onClick(() => {
            this.modifier.updateConstructorParams("文本也可以改变")
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

接口定义

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;
}

总结

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


更多关于HarmonyOS鸿蒙NEXT关于页面渲染的性能优化方案的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

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

更多关于HarmonyOS鸿蒙NEXT关于页面渲染的性能优化方案的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙NEXT中,页面渲染的性能优化主要从以下几个方面进行:

  1. UI组件优化:使用轻量级UI组件,避免过度嵌套和复杂的布局结构。减少不必要的组件刷新,通过@State@Prop等装饰器实现局部更新。

  2. 布局优化:采用高效的布局方式,如Flex布局,避免使用AbsoluteLayout等性能开销较大的布局。合理使用Visibility属性,减少不可见组件的渲染开销。

  3. 列表优化:在长列表渲染中,使用LazyForEachList组件的reuse机制,减少内存占用和渲染时间。通过onAppearonDisappear事件管理列表项的加载和卸载。

  4. 动画优化:使用Animation组件和Transition组件实现高效动画,避免使用setIntervalsetTimeout等JavaScript定时器触发动画。

  5. 资源优化:压缩图片资源,使用WebP格式替代PNGJPEG,减少内存占用。合理管理资源加载,避免一次性加载过多资源。

  6. 线程优化:将耗时操作放在Worker线程中执行,避免阻塞UI主线程。使用TaskPool进行多任务并行处理,提升整体性能。

  7. 事件优化:减少事件绑定的数量,使用事件委托机制,避免频繁触发事件回调。

  8. 缓存优化:利用StoragePreferences进行数据缓存,减少重复数据请求和渲染。

通过以上优化方案,可以有效提升HarmonyOS鸿蒙NEXT页面渲染的性能,确保流畅的用户体验。

在HarmonyOS鸿蒙NEXT中,页面渲染性能优化可从以下方面入手:

  1. 减少布局层级:使用扁平化布局,减少嵌套,提升渲染速度。
  2. 重用组件:通过RecyclerViewListContainer等组件重用机制,减少内存占用。
  3. 异步加载:将耗时操作(如网络请求、图片加载)放在后台线程,避免阻塞UI线程。
  4. 硬件加速:启用硬件加速,提升图形渲染效率。
  5. 资源优化:压缩图片、使用矢量图,减少资源加载时间。
  6. 避免过度绘制:使用Canvas.clipRect()等方法,减少不必要的绘制操作。

通过这些策略,可显著提升页面渲染性能,优化用户体验。

回到顶部