HarmonyOS Next中下面自定义组件有没有冗余刷新问题?

下面代码创建了自定义组件ComponentA、SpecialImage,每个组件都拥有一些状态变量和UI组件。组件ComponentA中存在Move和Scale两个按钮,在按钮的点击回调中改变状态变量的值刷新相应的组件。

// 常量声明
const animationDuration: number = 500; // move动画时长
const opacityChangeValue: number = 0.1; // opacity每次变化的值
const opacityChangeRange: number = 1; // opacity变化的范围
const translateYChangeValue: number = 180; // translateY每次变化的值
const translateYChangeRange: number = 250; // translateY变化的范围
const scaleXChangeValue: number = 0.6; // scaleX每次变化的值
const scaleXChangeRange: number = 0.8; // scaleX每次变化的值

// 样式属性类
class UIStyle {
  public translateX: number = 0;
  public translateY: number = 0;
  public scaleX: number = 0.3;
  public scaleY: number = 0.3;
}

@Component
struct ComponentA {
  @Link uiStyle: UIStyle; // uiStyle的属性被多个组件使用

  build() {
    Column() {
      // 使用状态变量的组件
      SpecialImage({ specialImageUiStyle: this.uiStyle })
      Stack() {
        Column() {
          Image($r('app.media.app_icon'))
            .height(78)
            .width(78)
            .scale({
              x: this.uiStyle.scaleX,
              y: this.uiStyle.scaleY
            })
        }

        Stack() {
          Text('Hello World')
        }
      }
      .translate({
        x: this.uiStyle.translateX,
        y: this.uiStyle.translateY
      })

      // 通过按钮点击回调修改状态变量的值,引起相应的组件刷新
      Column() {
        Button('Move')
          .onClick(() => {
            animateTo({ duration: animationDuration }, () => {
              this.uiStyle.translateY = (this.uiStyle.translateY + translateYChangeValue) % translateYChangeRange;
            })
          })
        Button('Scale')
          .onClick(() => {
            this.uiStyle.scaleX = (this.uiStyle.scaleX + scaleXChangeValue) % scaleXChangeRange;
          })
      }
    }
  }
}

@Component
struct SpecialImage {
  @Link specialImageUiStyle: UIStyle;
  private opacityNum: number = 0.5; // 默认透明度

  private isRenderSpecialImage(): number {
    // Image每次渲染时透明度增加0.1, 在0-1之间循环
    this.opacityNum = (this.opacityNum + opacityChangeValue) % opacityChangeRange;
    return this.opacityNum;
  }

  build() {
    Column() {
      Image($r('app.media.app_icon'))
        .size({ width: 200, height: 200 })
        .scale({
          x: this.specialImageUiStyle.scaleX,
          y: this.specialImageUiStyle.scaleY
        })
        .opacity(this.isRenderSpecialImage())
      Text("SpecialImage")
    }
  }
}

@Entry
@Component
struct DFXStateBeforeOptimization {
  @State uiStyle: UIStyle = new UIStyle();

  build() {
    Column() {
      ComponentA({
        uiStyle: this.uiStyle
      })
    }
    .width('100%')
    .height('100%')
  }
}

本文参与了 【 HarmonyOS NEXT 技术问答冲榜,等你来战!】欢迎正在阅读的你也加入。

阅读 539
avatarAI BotBETA
**答案**:
在HarmonyOS Next中,根据你提供的代码,**存在潜在的冗余刷新问题**。

**解释**:

1. **`SpecialImage` 组件中的冗余刷新**:
   - `SpecialImage` 组件中的 `isRenderSpecialImage` 方法每次在 `build` 方法中被调用时都会更新 `opacityNum` 的值。这意味着每次组件重新渲染(例如,当 `uiStyle` 中的任何属性发生变化时),`opacityNum` 都会更新,即使这种变化与 `opacity` 无关。
   - 如果 `uiStyle` 的变化非常频繁(例如,由于 `ComponentA` 中的按钮点击),则 `SpecialImage` 组件可能会因为 `uiStyle` 的变化而频繁重新渲染,从而导致 `opacityNum` 的不必要更新和可能的性能问题。

2. **优化建议**:
   - 考虑将 `opacityNum` 的更新与 `uiStyle` 的变化解耦。例如,可以使用一个定时器或动画来控制 `opacityNum` 的更新,而不是每次组件渲染时都更新它。
   - 如果 `opacityNum` 的更新确实需要与 `uiStyle` 的某些变化同步,可以考虑使用更精确的状态管理或事件机制来触发更新,而不是依赖于组件的重新渲染。
1 个回答

可以结合hidumper工具来介绍冗余刷新的问题定位。

  1. 首先在设备上打开应用,进入ComponentA组件所在的页面。
  2. 使用以下命令获取示例应用的窗口Id。当前运行的示例应用包名为performancelibrary,可以在输出结果中找到对应窗口名performancelibrary0的WinId,即为应用的窗口Id。或者当应用正处于前台运行时,Focus window的值就是应用的窗口Id。此处示例应用的窗口Id为11,后面的流程中使用的命令都需要指定窗口Id。

    hdc shell "hidumper -s WindowManagerService -a '-a'"

    image.png

  3. 基于上一步获取的窗口Id 11,使用-viewHierarchy命令携带-r 参数递归打印应用的自定义组件树。

    hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -viewHierarchy -r'"

    打印应用的自定义组件树结果如下:

    -----------------ViewPU Hierarchy-----------------  
    [-viewHierarchy, viewId=4, isRecursive=true]  
    |--Index[4]  
    -----------------ViewPU Hierarchy-----------------  
    [-viewHierarchy, viewId=53, isRecursive=true]  
    |--DFXStateManagementPage[53]  
      |--DFXStateManagementHome[55]  
    -----------------ViewPU Hierarchy-----------------  
    [-viewHierarchy, viewId=65, isRecursive=true]  
    |--DFXStateBeforeOptimizationPage[65]  
      |--DFXStateBeforeOptimization[67]  
     |--ComponentA[70]  
       |--SpecialImage[73]

    从结果中找到目标组件ComponentA,后面括号中的内容即为组件ComponentA的节点Id 70。

  4. 使用命令-stateVariables携带参数-viewId(参数的值为ComponentA的节点Id)获取自定义组件ComponentA中的状态变量信息。

    hdc shell "hidumper -s WindowManagerService -a '-w 11 -jsdump -stateVariables -viewId=70'"

    打印组件ComponentA的状态变量信息如下:

    --------------ViewPU State Variables--------------  
    [-stateVariables, viewId=70, isRecursive=false]  
    |--ComponentA[70]  
      @Link/@Consume (class SynchedPropertyTwoWayPU) 'uiStyle'[71]  
      |--Owned by @Component 'ComponentA'[70]  
      |--Sync peers: {  
     @Link/@Consume (class SynchedPropertyTwoWayPU) 'specialImageUiStyle'[74] <@Component 'SpecialImage'[73]>  
      }  
      |--Dependent components: 2 elmtIds: 'Stack[75]', 'Image[77]'

    结果显示ComponentA拥有@Link/@Consume类型的状态变量uiStyle。每条状态变量的详细信息都包含状态变量的所属组件、同步对象和关联组件。

  5. 以状态变量uiStyle为例。

① Sync peers表示uiStyle在自定义组件SpecialImage中存在@Link/@Consume类型的状态变量specialImageUiStyle订阅数据变化。

② Dependent components表示在ComponentA组件中存在组件Stack[75]Image[77]使用了状态变量uiStyle,关联组件的数量为2。

所以当uiStyle变化时,影响的组件范围为自定义组件SpecialImage以及系统组件Stack[75]Image[77]
image.png

本文参与了 【 HarmonyOS NEXT 技术问答冲榜,等你来战!】欢迎正在阅读的你也加入。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进