在ArkTS中,怎么优化布局以进步功能?
我们好,我是 V 哥。在鸿蒙原生运用开发中,当一个杂乱的界面加载数据或发生改动时,布局或许会发生调整,为了进步布局改动带来的功能问题,V 哥在实践开发中,总结了一些优化技巧,来进步布局功能,笔记共享给我们。
1. 防止不必要的二次布局
- 在Flex布局中,假如子组件的主轴尺度总和不等于容器的主轴尺度长度,或许需求进行二次布局。为了防止这种状况,能够保证子组件的主轴尺度总和等于容器的主轴尺度长度,或许合理设置
flexGrow
、flexShrink
特点,以削减不必要的布局重排。
在ArkTS中,二次布局一般发生在容器的子元素尺度或方位需求从头核算时。在测验中,V 哥发现以下一些状况或许导致二次布局。
场景 1:动态改动子元素尺度
示例代码:
@Entry
@Component
struct DynamicResizeExample {
private isLarge = false;
build() {
Column() {
Text("Toggle Size").onClick(() => {
this.isLarge = !this.isLarge;
}).padding(10);
Text("Dynamic Text").fontSize(this.isLarge ? 24 : 16).padding(10);
}
}
}
解析:
在这个比如中,有一个文本元素的字体大小会依据按钮的点击事情动态改动。当字体大小改动时,文本元素的尺度也会随之改动,这或许导致父容器(Column)需求从头布局以习惯新的尺度,然后发生二次布局。
场景 2:异步加载内容导致的尺度改动
示例代码:
@Entry
@Component
struct AsyncContentExample {
private content: string = '';
build() {
Column() {
Text("Load Content").onClick(() => {
this.loadContent();
}).padding(10);
Text(this.content).padding(10);
}
}
private async loadContent() {
// 模仿异步加载数据
this.content = await fetchContent();
this.update(); // 手动触发更新
}
private async fetchContent(): Promise<string> {
// 模仿网络恳求
return new Promise(resolve => {
setTimeout(() => {
resolve("Loaded Content");
}, 1000);
});
}
}
解析:
在这个比如中,点击按钮会异步加载内容,加载完结后更新文本元素的内容。由于异步加载的内容尺度或许与原始内容不同,这会导致父容器需求从头布局以习惯新的尺度,然后或许发生二次布局。
场景 3:运用Flex布局时子元素尺度不确定
示例代码:
@Entry
@Component
struct FlexLayoutExample {
private items: string[] = ['Item 1', 'Item 2', 'Item 3'];
build() {
Flex() {
this.items.forEach(item => {
Text(item).width('auto').margin(5);
});
}
}
}
解析:
在这个比如中,运用了Flex布局,而且每个文本元素的宽度设置为'auto',这意味着它们的宽度将依据内容主动调整。假如文本内容发生改动或许字体大小动态改动,这或许导致Flex容器需求从头核算子元素的尺度和方位,然后发生二次布局。
这些场景在ArkTS中或许导致二次布局的常见状况,一般与动态尺度改动、异步内容加载或不确定的子元素尺度有关。为了防止二次布局,能够采纳一些优化办法。下面我们来看一下怎么进行优化。
为了防止二次布局带来的功能问题,我们能够采纳以下优化办法:
场景 1:动态改动子元素尺度
优化办法:
- 运用
matchContent()
代替fontSize()
动态改动,以防止触发二次布局。 - 运用动画滑润过渡尺度改动,削减布局的触发。
优化后的代码:
@Entry
@Component
struct DynamicResizeExample {
private isLarge = false;
build() {
Column() {
Text("Toggle Size").onClick(() => {
this.isLarge = !this.isLarge;
}).padding(10);
Text("Dynamic Text").matchContent().fontSize(this.isLarge ? 24 : 16).padding(10).transition({ duration: 300 });
}
}
}
场景 2:异步加载内容导致的尺度改动
优化办法:
- 预先设定一个最大尺度,以削减因内容改动导致的布局调整。
- 运用占位符显现内容加载中的状况,防止内容忽然改动导致的布局调整。
优化后的代码:
@Entry
@Component
struct AsyncContentExample {
private content: string = '...Loading';
build() {
Column() {
Text("Load Content").onClick(() => {
this.loadContent();
}).padding(10);
Text(this.content).maxWidth(300).padding(10); // 预设最大宽度
}
}
private async loadContent() {
// 模仿异步加载数据
this.content = await fetchContent();
this.update(); // 手动触发更新
}
private async fetchContent(): Promise<string> {
// 模仿网络恳求
return new Promise(resolve => {
setTimeout(() => {
resolve("Loaded Content");
}, 1000);
});
}
}
场景 3:运用Flex布局时子元素尺度不确定
优化办法:
- 为Flex子项设置最小和最大宽度,削减因内容改动导致的布局调整。
- 运用
wrapContent()
代替width('auto')
,以习惯内容改动。
优化后的代码:
@Entry
@Component
struct FlexLayoutExample {
private items: string[] = ['Item 1', 'Item 2', 'Item 3'];
build() {
Flex() {
this.items.forEach(item => {
Text(item).wrapContent().minWidth(50).maxWidth(200).margin(5); // 设置最小和最大宽度
});
}
}
}
经过这些优化办法,我们能够削减因动态改动导致的二次布局,进步运用的功能和用户体会。这些办法包括运用动画过渡、预设尺度、设置最小和最大宽度等,以削减布局的不确定性和频频改动。
2. 优先运用layoutWeight特点
- 代替
flexGrow
特点和flexShrink
特点,layoutWeight
特点能够更有用地操控子组件的布局权重,然后进步布局功能。
原始代码事例(运用flexGrow
和flexShrink
)
在Flex布局中,假如子组件的主轴尺度长度总和小于容器主轴尺度长度,且包括设置有用的flexGrow
特点的子组件,这些子组件会触发二次布局,拉伸布局以填满容器。相同,假如子组件的主轴尺度长度总和大于容器主轴尺度长度,且包括设置有用的flexShrink
特点的子组件,这些子组件也会触发二次布局,紧缩布局以填满容器。
@Entry
@Component
struct OriginalFlexLayout {
build() {
Flex() {
Text("Item 1").flexGrow(1)
Text("Item 2").flexGrow(2)
}
}
}
假如容器空间缺乏以包容两个文本元素,它们会依据flexGrow
特点进行拉伸,或许导致二次布局。
优化后的代码(运用layoutWeight
)
运用layoutWeight
特点代替flexGrow
和flexShrink
特点能够更有用地操控子组件的布局权重,然后进步布局功能。layoutWeight
特点在分配剩下空间时,两次遍历都只布局一次组件,不会触发二次布局。
@Entry
@Component
struct OptimizedLayoutWithLayoutWeight {
build() {
Row() {
Text("Item 1").layoutWeight(1)
Text("Item 2").layoutWeight(2)
}
}
}
在这个优化后的代码中,我们运用了Row
布局和layoutWeight
特点来代替Flex
布局中的flexGrow
特点。这样,当容器空间缺乏以包容两个文本元素时,它们会依据layoutWeight
特点的份额分配空间,而不会引起二次布局。
运用layoutWeight
特点的首要优势在于它简化了布局核算进程。与flexGrow
和flexShrink
比较,layoutWeight
不需求在容器空间缺乏时进行额定的拉伸或紧缩核算,由于它直接依据权重分配空间。这种办法削减了布局的杂乱度,特别是在子组件数量较多或许布局较为杂乱的状况下,能够明显进步布局功能。经过这种办法,我们能够防止不必要的二次布局,然后进步运用的呼应速度和用户体会。
3. 呼应式布局规划
- 经过条件烘托和自定义Builder函数,能够创立习惯不同设备的界面,削减因设备差异导致的布局重排。
呼应式布局规划不妥或许会导致布局重排,特别是在不同屏幕尺度或方向改动时。以下是一个没有运用呼应式规划,导致在不同设备上或许呈现布局问题的原始代码事例:
原始代码事例
@Entry
@Component
struct ResponsiveLayoutIssue {
build() {
Column() {
Text("Header").fontSize(24) // 大标题
Row() {
Text("Menu Item 1") // 菜单项
Text("Menu Item 2") // 菜单项
Text("Menu Item 3") // 菜单项
}.width('100%') // 强制菜单宽度为100%
Text("Content") // 主内容区域
}
}
}
在这个比如中,Row
中的菜单项在小屏幕上或许会显现拥堵,由于它们被设置为width('100%')
,这或许导致布局重排和内容溢出。
优化后的代码
为了优化上述代码,我们能够选用条件烘托和自定义Builder函数来创立习惯不同设备的界面:
@Entry
@Component
struct ResponsiveLayoutOptimized {
build() {
Column() {
Text("Header").fontSize(24) // 大标题
if (this.isSmallScreen()) {
// 小屏幕上,菜单项笔直摆放
ForEach(['Menu Item 1', 'Menu Item 2', 'Menu Item 3'], (item) => {
Text(item).width('100%') // 每个菜单项宽度为100%
})
} else {
// 大屏幕上,菜单项水平摆放
Row() {
Text("Menu Item 1") // 菜单项
Text("Menu Item 2") // 菜单项
Text("Menu Item 3") // 菜单项
}.width('100%') // 菜单全体宽度为100%
}
Text("Content") // 主内容区域
}
}
isSmallScreen() {
// 假定这个函数依据屏幕尺度回来true或false
return window.width < 600; // 假定宽度小于600px为小屏幕
}
}
功能优化解说
运用呼应式布局规划的首要优势在于它能够依据设备的屏幕尺度和方向动态调整布局,削减因设备差异导致的布局重排:
- 削减布局重排:经过条件烘托,我们能够为不同屏幕尺度供给不同的布局结构,然后削减因屏幕尺度改动导致的布局重排。
- 进步用户体会:习惯不同设备的界面能够供给更好的用户体会,尤其是在移动设备和桌面设备之间切换时。
- 优化功能:削减布局核算和重排能够进步运用的功能,尤其是在动态内容更新和设备方向改动时。
所以,经过呼应式布局规划,我们能够创立习惯不同设备的界面,削减因设备差异导致的布局重排,然后进步运用的功能和用户体会。
4. 懒加载
- 关于杂乱或资源密集型的组件,运用懒加载能够进步运用的发动速度,仅在需求时才加载和烘托这些组件。
懒加载带来的问题
在运用开发中,假如一切组件都在运用发动时一次性加载,或许会导致发动时刻过长,特别是关于包括杂乱或资源密集型组件的运用。这会影响用户体会,由于用户需求等候一切资源加载完结后才干运用运用。
原始代码事例
以下是一个没有运用懒加载的原始代码事例,其间包括一个大型列表,列表中的每个项都是一个杂乱的组件:
@Entry
@Component
struct NonLazyLoadingExample {
private items: any[] = this.generateItems(100); // 假定生成了100个杂乱项
generateItems(count: number): any[] {
let items: any[] = [];
for (let i = 0; i < count; i++) {
items.push({
id: i,
// 假定每个项包括许多数据和资源
data: this.generateLargeData()
});
}
return items;
}
generateLargeData(): any {
// 生成许多数据的模仿函数
return new Array(1000).fill(null).map((_, index) => ({ index }));
}
build() {
Column() {
ForEach(this.items, (item) => {
// 烘托杂乱组件,假定每个组件都需求加载许多资源
this.renderComplexItem(item);
});
}
}
renderComplexItem(item: any) {
// 杂乱组件的烘托逻辑
Column() {
Text(`Item ID: ${item.id}`).fontWeight(FontWeight.Bold);
// 假定这里有更多杂乱的UI和资源加载
}
}
}
在这个比如中,一切杂乱组件都在运用发动时一次性烘托,这或许导致运用发动缓慢。
优化后的代码
运用懒加载,我们能够只在组件需求显现时才加载和烘托它们。以下是运用懒加载优化后的代码:
@Entry
@Component
struct LazyLoadingOptimizedExample {
private items: any[] = this.generateItems(100); // 假定生成了100个杂乱项
generateItems(count: number): any[] {
let items: any[] = [];
for (let i = 0; i < count; i++) {
items.push({
id: i,
// 假定每个项包括许多数据和资源
data: this.generateLargeData()
});
}
return items;
}
generateLargeData(): any {
// 生成许多数据的模仿函数
return new Array(1000).fill(null).map((_, index) => ({ index }));
}
build() {
Column() {
// 运用懒加载ForEach
LazyForEach(this.items, (item) => {
// 仅在需求时才烘托杂乱组件
this.renderComplexItem(item);
});
}
}
renderComplexItem(item: any) {
// 杂乱组件的烘托逻辑
Column() {
Text(`Item ID: ${item.id}`).fontWeight(FontWeight.Bold);
// 假定这里有更多杂乱的UI和资源加载
}
}
}
功能优化解说
运用懒加载的首要优势在于:
- 进步发动速度:运用不需求在发动时加载一切资源,然后削减了发动时刻。
- 削减内存耗费:懒加载能够削减一起加载的资源数量,然后削减内存耗费。
- 按需加载:只要当用户滚动到某个部分时,相关的组件才会被加载和烘托,这进步了资源的运用功率。
- 改进用户体会:用户能够更快地看到运用的初始内容,而不需求等候一切资源加载完结。
所以要牢记,经过运用懒加载,我们能够优化运用的发动速度和功能,进步用户体会。
5. 优化大型目标的更新
- 关于包括多个特点的杂乱目标,运用
@Observed
和@ObjectLink
能够完成细粒度的更新,只要发生改动的部分会触发UI更新,而不是整个目标。
在ArkTS中,假如一个大型目标的特点发生改动时,没有运用细粒度更新,那么整个目标或许会被从头烘托,这会导致功能问题,尤其是在目标特点许多或许目标很大时。
原始代码事例
以下是一个没有运用@Observed
和@ObjectLink
的原始代码事例,其间包括一个大型目标,每次目标更新时,整个目标都会被从头烘托:
@Entry
@Component
struct LargeObjectUpdateIssue {
private largeObject: any = this.createLargeObject();
createLargeObject(): any {
// 创立一个包括多个特点的大型目标
let obj: any = {};
for (let i = 0; i < 100; i++) {
obj[`property${i}`] = `value${i}`;
}
return obj;
}
build() {
Column() {
Text(`Property1: ${this.largeObject.property1}`)
Text(`Property2: ${this.largeObject.property2}`)
// ...更多特点
}
}
updateObject() {
// 更新目标的某个特点
this.largeObject.property1 = 'new value';
// 这将导致整个目标从头烘托
}
}
在这个比如中,updateObject
办法更新了largeObject
的一个特点,但由于没有运用细粒度更新,整个目标都会被从头烘托。
优化后的代码
运用@Observed
和@ObjectLink
,我们能够只更新发生改动的部分,而不是整个目标:
@Entry
@Component
struct LargeObjectUpdateOptimized {
@Observed largeObject: any = this.createLargeObject();
createLargeObject(): any {
// 创立一个包括多个特点的大型目标
let obj: any = {};
for (let i = 0; i < 100; i++) {
obj[`property${i}`] = `value${i}`;
}
return obj;
}
build() {
Column() {
Text(`Property1: ${this.largeObject.property1}`)
Text(`Property2: ${this.largeObject.property2}`)
// ...更多特点
}
}
updateObject() {
// 更新目标的某个特点
this.largeObject.property1 = 'new value';
// 只要property1相关的UI会更新
}
}
功能优化解说
运用@Observed
和@ObjectLink
的首要优势在于:
- 削减不必要的烘托:只要发生改动的部分会触发UI更新,而不是整个目标,这削减了不必要的烘托。
- 进步功能:削减了烘托次数,进步了运用的功能,尤其是在目标特点许多或许目标很大时。
- 优化用户体会:用户界面的呼应更快,由于只要相关的部分被更新,而不是整个目标。
- 下降内存耗费:削减了由于烘托整个目标而或许发生的额定内存耗费。
所以,经过运用@Observed
和@ObjectLink
,我们能够优化大型目标的更新,进步运用的功能和用户体会。
6. 内存办理和防止内存走漏
- 运用目标池形式办理频频创立和毁掉的目标,如粒子体系、游戏中的敌人等,能够削减内存压力并进步功能。
内存办理和防止内存走漏的问题
在运用开发中,频频创立和毁掉目标(如粒子体系、游戏中的敌人等)或许会导致内存压力增大,并添加废物收回(GC)的频率,然后影响运用功能。此外,假如目标没有被正确毁掉,还或许导致内存走漏。
原始代码事例
以下是一个没有运用目标池形式的原始代码事例,其间包括频频创立和毁掉目标的操作:
@Entry
@Component
struct MemoryLeakExample {
private enemies: any[] = [];
createEnemy() {
// 模仿创立一个敌人目标
let enemy = {
id: Date.now(),
name: `Enemy ${this.enemies.length + 1}`,
// 假定这里有更多杂乱的特点和办法
};
this.enemies.push(enemy);
}
build() {
Button("Create Enemy").onClick(() => {
this.createEnemy();
});
}
}
在这个比如中,每次点击按钮都会创立一个新的敌人目标,并将其添加到数组中。假如这些目标没有被正确办理,或许会导致内存走漏。
优化后的代码
运用目标池形式,我们能够重用目标,削减内存压力并进步功能:
@Entry
@Component
struct MemoryManagementOptimized {
private enemyPool: any[] = []; // 目标池
createEnemy() {
if (this.enemyPool.length > 0) {
// 从目标池中获取一个目标
let enemy = this.enemyPool.pop();
enemy复生();
this.enemies.push(enemy);
} else {
// 创立一个新的敌人目标
let enemy = {
id: Date.now(),
name: `Enemy ${this.enemies.length + 1}`,
// 假定这里有更多杂乱的特点和办法
复生: () => {
// 复生逻辑,重置目标状况
}
};
this.enemies.push(enemy);
}
}
destroyEnemy(enemy: any) {
// 将敌人目标状况重置后放回目标池
enemy.复生();
this.enemyPool.push(enemy);
}
build() {
Button("Create Enemy").onClick(() => {
this.createEnemy();
});
Button("Destroy Enemy").onClick(() => {
if (this.enemies.length > 0) {
let enemy = this.enemies.pop();
this.destroyEnemy(enemy);
}
});
}
}
功能优化解说
运用目标池形式的首要优势在于:
- 削减内存压力:经过重用目标,削减了频频创立和毁掉目标导致的内存压力。
- 进步功能:削减了废物收回(GC)的频率,然后进步了运用的功能。
- 防止内存走漏:经过正确办理目标的生命周期,防止了内存走漏的危险。
- 进步资源利用率:目标池能够进步目标的利用率,削减资源糟蹋。
我们经过运用目标池形式,能够优化内存办理,削减内存压力,进步运用的功能,并防止内存走漏。
最终
以上 V 哥总结的小小优化心得,能够有用地优化ArkTS中的布局功能,进步运用的呼应速度和用户体会。欢迎重视威哥爱编程,鸿蒙开发你我他。