本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、布局优化的核心目标
- 减少渲染层级:避免嵌套过深的组件树。
- 降低布局复杂度:优先使用高效布局(如
Flex
替代多层Row/Column
)。 - 复用组件:避免重复创建相同UI结构。
- 按需加载:动态加载非首屏内容。
二、常用优化方法
1. 选择高效布局容器
布局容器 | 适用场景 | 优化优势 |
---|---|---|
Flex | 多元素对齐、权重分配 | 减少嵌套,GPU渲染效率高 |
Grid | 网格布局(如相册、商品列表) | 替代多层Row/Column 嵌套 |
List | 长列表数据 | 自带复用机制,内存占用恒定 |
示例:Flex
替代嵌套Row/Column
// ❌ 不推荐:多层嵌套
Column() {
Row() {
Image($r('app.media.icon'))
Column() {
Text('标题')
Text('副标题')
}
}
}
// ✅ 推荐:Flex布局
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
Image($r('app.media.icon'))
Flex({ direction: FlexDirection.Column }) {
Text('标题')
Text('副标题')
}
}
2. 避免过度绘制
- 问题:重叠组件导致GPU重复绘制。
- 解决:使用
clip
属性限制绘制区域。
示例:裁剪超出内容
Stack() {
Text('长文本...').fontSize(16)
}
.width(100)
.height(50)
.clip(true) // 裁剪超出部分
3. 复用组件:@Reusable
装饰器
对频繁使用的组件标记@Reusable
,系统自动缓存实例。
@Reusable
@Component
struct ReusableButton {
@Prop text: string
build() {
Button(this.text)
.width(100)
.height(40)
}
}
// 使用
Column() {
ForEach(this.buttons, (item) => {
ReusableButton({ text: item })
})
}
4. 动态加载:LazyForEach
适用于长列表数据,仅渲染可视区域内的项。
@Entry
@Component
struct LazyListExample {
@State data: string[] = ['Item 1', 'Item 2', ... 'Item 100'];
build() {
List() {
LazyForEach(this.data, (item: string) => {
ListItem() {
Text(item).fontSize(20)
}
})
}
}
}
5. 条件渲染:减少DOM节点
使用if/else
替代visibility
隐藏,彻底移除不可见节点。
Column() {
if (this.showHeader) { // 条件为false时完全移除节点
Text('标题').fontSize(18)
}
}
- 使用if条件判断切换显示时,组件会因为条件改变而判断是否参与创建、布局过程,切换过程会出现较大的Measure的性能消耗,原因是创建了新的组件,重新进行了Measure和Layout的过程。
- 使用visibility的情况下,无论是否隐藏,组件在初次已经创建完成,并一直都存在组件树上,不会出现组件重新创建的过程,并且在Measure和Layout阶段的性能消耗比使用if/else的方式性能小很多,原因是组件的计算在首帧时已经计算过,不需要重复计算。
三、性能分析工具
1. ArkUI Inspector
- 功能:查看UI层级、帧率、内存占用。
- 使用:DevEco Studio → Tools → ArkUI Inspector。
2. 布局耗时统计
aboutToAppear() {
console.time('layoutTime');
}
onPageShow() {
console.timeEnd('layoutTime'); // 输出布局耗时
}
四、高级优化技巧
1. 离屏绘制:Canvas
预处理
复杂图形提前绘制到离屏Canvas,避免实时计算。
@State offscreenCanvas: CanvasRenderingContext2D = null;
aboutToAppear() {
this.offscreenCanvas = /* 初始化并绘制 */;
}
build() {
Canvas(this.offscreenCanvas) // 直接复用
}
2. 布局预热
非首屏内容提前构建但隐藏,切换时快速显示。
Column() {
// 首屏内容
this.buildMainContent()
// 非首屏(提前构建但隐藏)
if (this.preloadDone) {
this.buildSecondaryContent()
}
}
3. GPU加速
对动画/滚动元素启用GPU加速:
Text('高性能渲染')
.transform({ perspective: 1000 }) // 触发GPU加速
.transition('opacity', { duration: 300 })
五、常见问题
问题 | 原因 | 解决方案 |
---|---|---|
列表卡顿 | 未使用LazyForEach | 替换ForEach 为LazyForEach |
内存泄漏 | 未解绑事件监听 | aboutToDisappear 中移除监听 |
布局闪烁 | 频繁重建组件 | 使用@Reusable 或状态管理 |
六、总结
- 原则:
- 减少层级 > 优化样式 > 动态加载。
- 首屏优先加载,非关键内容延迟渲染。
- 工具链:
- 开发阶段开启
ArkUI Inspector
实时监控。 - 使用
hilog
记录关键性能指标。
- 开发阶段开启
- 代码规范:
- 避免在
build()
中执行耗时操作。 - 对重复UI结构提取为
@Component
。
- 避免在