避免在生命周期函数执行耗时操作
在build函数执行之前,将先执行aboutToAppear()生命周期回调函数。若在该函数中执行耗时操作,将阻塞UI渲染,增加UI主线程负担。可以将耗时操作放到子线程或者使用不耗时的函数。例如在aboutToAppear中应该避免使用ResourceManager的getXXXSync接口入参中直接使用资源信息,推荐使用资源id作为入参,推荐用法为:resourceManager.getStringSync($r('app.string.test').id)。第一种方式获取的是拷贝对象,发生了一次深拷贝,第二种方式直接获取原对象的引用。
使用@Builder方法代替自定义组件
在ArkUI中使用自定义组件时,在build阶段将在后端FrameNode树创建一个相应的CustomNode节点,在渲染阶段时也会创建对应的RenderNode节点,如下图所示。 减少自定义组件的使用,尤其是自定义组件在循环中的使用,将成倍减少FrameNode节点树上CustomNode节点数量,有效缩短页面的加载和渲染时长。@Builder函数不会在后端FrameNode节点树上创建一个新的树节点。如果组件仅仅是用来纯展示,不需要更新,优先使用@Builder函数代替自定义组件。
按需注册组件属性
组件每个属性保存在FrameNode节点上,组件设置了大量属性且该组件被大量使用,对应用的整体性能会产生较大影响。应按需注册组件属性,避免设置冗余属性。如果必须设置很多属性,可以考虑AttributeModifier动态注册组件属性的方式。动态注册组件属性可以实现差异更新属性,当组件创建或者更新时,重新执行组件的样式属性对象的更新接口。通过key找到对应的属性修改器对象进行差异对比,若有更新变化再通知native侧进行属性更新。
scss
// 1.自定义属性修改器,该类实现了AttributeModifier接口
class RowModifier implements AttributeModifier<RowAttribute> {
private customImage: ResourceStr = '';
private static instance: RowModifier;
constructor() {}
setCustomImage(customImage: ResourceStr) {
this.customImage = customImage;
return this;
}
// 采用单例模式,避免为每个组件都创建一个新的修改器,增加创建产生的性能开销
public static getInstance(): RowModifier {
if (!RowModifier.instance) {
RowModifier.instance = new RowModifier();
}
return RowModifier.instance;
}
// 2.实现AttributeModifier接口的applyNormalAttribute方法,自定义属性设置的逻辑
applyNormalAttribute(instance: RowAttribute) {
if (this.customImage) {
instance.backgroundImage(this.customImage);
instance.backgroundImageSize(ImageSize.Cover);
} else {
instance.backgroundColor(DEFAULT_BACKGROUND_COLOR);
instance.justifyContent(FlexAlign.Center);
// instance.padding(2)
// instance.margin(2)
// instance.opacity(1)
// instance.clip(false)
// instance.layoutWeight(1)
// instance.backgroundBlurStyle(BlurStyle.NONE)
// instance.alignItems(VerticalAlign.Center)
// instance.borderWidth(1)
// instance.borderColor(Color.Pink)
// instance.borderStyle(BorderStyle.Solid)
// instance.expandSafeArea([SafeAreaType.SYSTEM])
// instance.rotate({ angle: 5 })
// instance.responseRegion({x: 0})
//instance.mouseResponseRegion({x: 0})
// instance.constraintSize({minWidth: 25})
// instance.hitTestBehavior(HitTestMode.Default)
//instance.backgroundImagePosition(Alignment.Center)
//instance.foregroundBlurStyle(BlurStyle.NONE)
}
instance.size({ width: 50, height: 50 });
instance.borderRadius(25);
}
}
@Component
struct Avatar {
@ObjectLink user: User;
build() {
Row() {
if (!this.user.avatarImage) {
Text(this.user.name.charAt(0))
.fontSize(28)
.fontColor(Color.White)
.fontWeight(FontWeight.Bold)
}
}
// 3.将自定义RowModifier类作为参数传入,实现按需注册属性
.attributeModifier(RowModifier.getInstance().setCustomImage(this.user.avatarImage))
}
}
精简节点数
移除冗余的节点,可能会在Row容器包含一个Row容器。如下代码,Row容器Row容器,这种嵌套实际是多余的,并且会给布局层次结构造成不必要的开销。
scss
Row() {
Row(){
Image()
Text()
}
Image()
}
修改后的代码
scss
Row() {
Image()
Text()
Image()
}
合理使用布局容器组件
- 在相同嵌套层级的情况下,如果多种布局方式可以实现相同布局效果,优选低耗时的布局,如使用Column、Row替代Flex实现相同的单行布局。
- 在能够通过其他布局大幅优化节点数的情况下,可以使用高级组件替代,如使用RelativeContainer替代Row、Column实现扁平化布局,此时其收益大于布局组件本身的性能差距。
- 仅在必要的场景下使用高耗时的布局组件,如使用Flex实现折行布局、使用Grid实现二维网格布局等。
利用布局边界减少布局计算
如果组件的宽高不需要自适应,那就写死宽高。当其组件外部的容器尺寸发生变化时,组件本身的宽高固定,就不需要重新测量。
合理控制元素显示与隐藏
使用Visibility.None、if条件判断等都能够元素显示与隐藏。如果频繁修改元素的显示与隐藏,通过visibility属性控制,可以省去组件创建的时间,直接进入渲染过程。交互次数很少的情况下,使用if条件判断来控制元素的显示与隐藏效果,对于内存有较大提升。
Scroll嵌套List场景下,给定List组件宽高
Scroll嵌套List时:
- 给list组件设置宽高,只有布局区域内的子组件会参与布局。不设置宽高,所有子组件都会参与布局。
- List使用ForEach加载子组件时,无论是否设置List的宽高,都会加载所有子组件。
- List使用LazyForEach加载子组件时,没有设置List的宽高,会加载所有子组件,设置了List的宽高,会加载List显示区域内的子组件。
优先使用组件属性代替嵌套组件
在实现文本浮层、按压遮罩或颜色叠加等场景时,通常会采用Stack布局嵌套组件的方式。实际上有些场景直接使用组件属性或借助系统API的能力就能实现,例如使用overlay属性可以实现浮层场景,使用ColorMetrics可以实现颜色叠加效果。直接使用组件属性可以减少Stack布局嵌套组件,减少组件节点数。