👋 你好,欢迎来到我的博客!我是【菜鸟学鸿蒙】
   我是一名在路上的移动端开发者,正从传统“小码农”转向鸿蒙原生开发的进阶之旅。为了把学习过的知识沉淀下来,也为了和更多同路人互相启发,我决定把探索 HarmonyOS 的过程都记录在这里。
  
  🛠️ 主要方向:ArkTS 语言基础、HarmonyOS 原生应用(Stage 模型、UIAbility/ServiceAbility)、分布式能力与软总线、元服务/卡片、应用签名与上架、性能与内存优化、项目实战,以及 Android → 鸿蒙的迁移踩坑与复盘。
  🧭 内容节奏:从基础到实战——小示例拆解框架认知、专项优化手记、实战项目拆包、面试题思考与复盘,让每篇都有可落地的代码与方法论。
  💡 我相信:写作是把知识内化的过程,分享是让生态更繁荣的方式。
  
   如果你也想拥抱鸿蒙、热爱成长,欢迎关注我,一起交流进步!🚀

本文会用:首屏渲染、状态刷新控制、避免不必要重绘、调试工具四条主线,把“能立刻落地”的实战招式讲透。涉及工具与关键机制会引用官方文档:DevEco Profiler、HiTraceMeter、SmartPerf-Host FrameTimeline、LazyForEach 与 @Reusable 等。(华为开发者网站)

前言:先把一句“狠话”钉在墙上🧷

性能优化不是让你写更少,而是让框架干更少。
ArkUI 是声明式,状态一变就可能触发 UI 重建。你要做的不是“禁止刷新”(做不到也不该),而是:

  • 刷新得更少(少更新状态)
  • 刷新得更小(缩小刷新范围)
  • 刷新得更聪明(列表复用、按需加载)
  • 能量化(用工具把“感觉卡”变成“哪里卡”)(华为开发者网站)

1) 首屏渲染优化:先让用户“看到东西”,再让它“变好看”😄

首屏优化的核心目标其实就一个:更快到首帧。你可以用两段式策略:

1.1 首屏“两段式”渲染:骨架屏 + 异步填充

**坏写法(别学我当年😭):**首屏生命周期里同步干重活,UI 迟迟出不来。

**好写法:**先画骨架(可见),再异步拉数据填充(变完整)。

@Entry
@Component
struct HomePage {
  @State loading: boolean = true
  @State title: string = ''
  @State list: Array<string> = []

  async aboutToAppear() {
    // 先给用户一个“活着”的界面,再慢慢补齐
    await this.loadData()
  }

  private async loadData() {
    this.loading = true
    // 模拟:请求/读库/计算
    await new Promise<void>(r => setTimeout(() => r(), 400))
    this.title = '欢迎回来~今天也别让首屏等太久🙂'
    this.list = Array.from({ length: 40 }).map((_, i) => `Item #${i}`)
    this.loading = false
  }

  build() {
    Column({ space: 12 }) {
      if (this.loading) {
        Skeleton()
      } else {
        Text(this.title).fontSize(22).fontWeight(FontWeight.Bold)
        ListArea({ list: this.list })
      }
    }
    .padding(16)
    .width('100%')
    .height('100%')
  }
}

@Component
struct Skeleton {
  build() {
    Column({ space: 10 }) {
      Text('加载中…别急,我在跑🏃').opacity(0.7)
      // 这里可以用几个灰块模拟骨架
      ForEach([1, 2, 3, 4], _ => {
        Row().height(18).width('100%').backgroundColor('#eee').borderRadius(6)
      })
    }
  }
}

1.2 长列表首屏:别用 ForEach “一口吞”,用 LazyForEach “分口吃”🍜

大数据量列表,直接 ForEach 一次性构建所有节点,很容易把首屏拖死;LazyForEach 是按需渲染,更适合长列表。(华为开发者网站)

@Component
struct ListArea {
  list: Array<string> = []

  build() {
    List() {
      // 真实项目里你会配合 DataSource,这里只做思路演示
      LazyForEach(this.list, (item: string) => {
        ListItem() {
          Text(item).padding(14)
        }
      })
    }
  }
}

重点:LazyForEach 的子节点离开可视区不会立刻回收,会在空闲时析构/回收,这是它能“稳帧”的关键之一。(华为开发者网站)

2) 状态刷新控制:别让 UI 因为“没必要的变更”疯狂重建😤

声明式 UI 里最常见的“隐形性能杀手”就是:状态更新频率过高,或者更新了但值根本没变

2.1 “值没变就别 set”:别让无效刷新白白发生🙃

@Component
struct ColorDemo {
  @State color: string = 'blue'

  private setRed() {
    // 关键:先判断,避免无意义的刷新
    if (this.color !== 'red') {
      this.color = 'red'
    }
  }

  build() {
    Column({ space: 10 }) {
      Text(`color = ${this.color}`)
      Button('变红(但别重复刷)').onClick(() => this.setRed())
    }.padding(16)
  }
}

这招看着小,但对“高频点击/高频回调”的场景非常救命(比如滚动联动、输入联想、动画控制)。

2.2 “把大状态拆小”:缩小刷新范围,别一动就全家重建😮‍💨

**反面例子:**一个大对象塞在一个状态里,改个字段导致大范围重建。

更好的做法:

  • 把状态拆到更靠近使用它的组件
  • 父组件尽量只管“路由/骨架/大结构”
  • 子组件各自维护局部状态(或者接收 props)

你要的不是“完全不刷新”,而是“只刷新该刷的那一小块”。

3) 避免不必要重绘:让列表“复用”,让复杂 UI “别叠罗汉”🧱

3.1 列表复用:LazyForEach + @Reusable,直接把“创建/销毁”成本砍掉一截✂️

官方明确:@Reusable 标记的组件支持视图节点、组件实例和状态上下文复用,避免重复创建销毁,提升性能;并且从 API 10 开始支持。(华为开发者网站)
另外,LazyForEach 和 @Reusable 搭配可以触发节点复用。(华为开发者网站)

实战写法:

@Reusable
@Component
struct RowItem {
  label: string = ''

  // 复用相关生命周期:框架会在上下树时回调
  aboutToReuse(params: Record<string, Object>) {
    // 复用时更新必要展示数据(别做重活)
    this.label = (params['label'] as string) ?? this.label
  }

  aboutToRecycle() {
    // 可做轻量清理(比如取消短期临时状态)
  }

  build() {
    Row() {
      Text(this.label).padding(14)
    }.width('100%')
  }
}

@Component
struct HugeList {
  private data: Array<string> = Array.from({ length: 10000 }).map((_, i) => `Row #${i}`)

  build() {
    List() {
      LazyForEach(this.data, (it: string) => {
        ListItem() {
          RowItem({ label: it })
        }
      })
    }
  }
}

小提醒:@Reusable 有限制条件(比如仅用于自定义组件等),而且会触发 aboutToReuse/aboutToRecycle 这些复用生命周期,代码集中写在这里最顺手。(华为开发者网站)

3.2 避免“高频变化 + 深层组件树”:能扁平就扁平,能合并就合并😅

当你遇到:

  • 多层嵌套容器(Column/Row/Stack 叠到像千层饼)
  • 透明叠加、阴影、渐变、复杂裁剪同时出现
  • 高频状态变化(例如每 16ms 更新)

那就别硬撑“全用组件堆出来”。你可以考虑:

  • 降低层级
  • 把高频绘制区域交给 Canvas(适合图表/特效/自绘)
  • 动画尽量用框架/系统动画能力(减少手动高频 setState)

(这条是“经验型结论”,你一做复杂页面就会懂:组件堆得越复杂,渲染管线越容易喘不过气😮‍💨)

4) 性能调试工具使用:别靠“我感觉”,用数据狠狠干它🔧

4.1 DevEco Profiler:用“泳道图”定位卡顿时间段

DevEco Profiler 以泳道单元组织性能数据,支持框选时间段看详情,推荐按 Top-Down(自顶向下)分析定位问题。(华为开发者网站)
我的常用打法是:

  1. 先录制一次“从冷启动到首屏可交互”的会话
  2. 盯 Frame/CPU/主线程相关泳道
  3. 框选卡顿区间 → 看函数栈/耗时事件 → 回到代码点名批评

4.2 HiTraceMeter:关键路径打点,把“首屏慢”拆成“哪段慢”🪡

官方说明:HiTraceMeter 提供系统性能打点接口(ArkTS),用于跟踪关键代码段。(华为开发者网站)

首屏打点示例:

import hiTraceMeter from '@ohos.hiTraceMeter'

@Entry
@Component
struct TraceHome {
  @State title: string = '...'

  async aboutToAppear() {
    hiTraceMeter.startTrace('home_prepare', 1001)
    await new Promise<void>(r => setTimeout(() => r(), 300))
    this.title = '首屏数据就绪✅'
    hiTraceMeter.finishTrace('home_prepare', 1001)
  }

  build() {
    Column() { Text(this.title).fontSize(20) }.padding(16)
  }
}

你会惊喜地发现:
以前你只知道“慢”,现在你能知道“慢在 home_prepare 里的哪一段”。

4.3 SmartPerf-Host FrameTimeline:专治“掉帧/卡顿帧”😤

OpenHarmony 文档提到:SmartPerf-Host 提供 FrameTimeline 帧率分析,能抓取每一帧渲染数据,自动标识卡顿帧,并结合同时段系统 Trace 辅助定位原因。(GitCode)
如果你遇到“滑动时忽快忽慢”的那种恶心卡顿,用它特别对症。

最后一段(我很想你记住):优化顺序别搞反了🙂

我建议你按这个优先级来,基本不会走弯路:

  1. 先量化:Profiler/Trace/FrameTimeline,把卡顿区间框出来(华为开发者网站)
  2. 再控刷新:减少无效状态更新、拆分状态范围
  3. 再做列表与复用:LazyForEach + @Reusable,把创建销毁的税降下来(华为开发者网站)
  4. 最后才是“微优化”:某行代码、某个算法、某个小布局

📝 写在最后

如果你觉得这篇文章对你有帮助,或者有任何想法、建议,欢迎在评论区留言交流!你的每一个点赞 👍、收藏 ⭐、关注 ❤️,都是我持续更新的最大动力!

我是一个在代码世界里不断摸索的小码农,愿我们都能在成长的路上越走越远,越学越强!

感谢你的阅读,我们下篇文章再见~👋

✍️ 作者:某个被流“治愈”过的 移动端 老兵
📅 日期:2025-11-05
🧵 本文原创,转载请注明出处。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐